summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLangston Smith <langston.smith@mapbox.com>2018-01-04 11:15:50 -0800
committerGitHub <noreply@github.com>2018-01-04 11:15:50 -0800
commit2ea955d2751ba6459f99a0695e53505c0a11702b (patch)
treef54450918b634a2eea1bd2c4ebc671bf1bb06106
parentf2ec6ae326bad79fea2b06a21151a2835522572a (diff)
parentc62b0af24fc76b4bb2eb34100611dd3ee9ee5536 (diff)
downloadqtlocation-mapboxgl-upstream/ls-android-readme-tweaks.tar.gz
Merge branch 'master' into ls-android-readme-tweaksupstream/ls-android-readme-tweaks
-rw-r--r--.gitignore5
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml8
-rw-r--r--.tx/config7
-rw-r--r--.ycm_extra_conf.py2
-rw-r--r--CHANGELOG.md4
-rw-r--r--CMakeLists.txt91
-rw-r--r--CODE-OF-CONDUCT.md5
-rw-r--r--CONTRIBUTING.md9
-rw-r--r--DEVELOPING.md123
-rw-r--r--INSTALL.md29
-rw-r--r--LICENSE.md2
-rw-r--r--Makefile174
-rw-r--r--README.md17
-rw-r--r--benchmark/api/query.benchmark.cpp22
-rw-r--r--benchmark/api/render.benchmark.cpp78
-rw-r--r--benchmark/fixtures/api/cache.dbbin405504 -> 1298432 bytes
-rw-r--r--benchmark/fixtures/api/query_style.json11313
-rw-r--r--benchmark/fixtures/api/style.json3993
-rw-r--r--benchmark/function/camera_function.benchmark.cpp61
-rw-r--r--benchmark/function/composite_function.benchmark.cpp69
-rw-r--r--benchmark/function/source_function.benchmark.cpp63
-rw-r--r--benchmark/parse/filter.benchmark.cpp8
-rw-r--r--benchmark/parse/tile_mask.benchmark.cpp38
-rw-r--r--benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp41
-rw-r--r--benchmark/src/mbgl/benchmark/util.cpp24
-rw-r--r--benchmark/src/mbgl/benchmark/util.hpp13
-rw-r--r--benchmark/util/dtoa.benchmark.cpp66
-rw-r--r--bin/offline.cpp76
-rw-r--r--bin/render.cpp145
-rw-r--r--circle.yml884
-rw-r--r--cmake/benchmark-files.cmake13
-rw-r--r--cmake/benchmark.cmake15
-rw-r--r--cmake/core-files.cmake154
-rw-r--r--cmake/core.cmake10
-rw-r--r--cmake/executable.xcscheme (renamed from platform/macos/scripts/executable.xcscheme)46
-rw-r--r--cmake/filesource.cmake49
-rw-r--r--cmake/glfw.cmake34
-rw-r--r--cmake/library.xcscheme (renamed from platform/macos/scripts/library.xcscheme)26
-rw-r--r--cmake/loop-darwin.cmake9
-rw-r--r--cmake/loop-uv.cmake7
-rw-r--r--cmake/mbgl.cmake39
-rw-r--r--cmake/node.cmake66
-rw-r--r--cmake/node.xcscheme (renamed from platform/macos/scripts/node.xcscheme)38
-rw-r--r--cmake/offline.cmake22
-rw-r--r--cmake/render.cmake28
-rw-r--r--cmake/test-files.cmake23
-rw-r--r--cmake/test.cmake18
-rw-r--r--cmake/xcode.cmake75
-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/actor/actor.hpp27
-rw-r--r--include/mbgl/actor/actor_ref.hpp21
-rw-r--r--include/mbgl/actor/message.hpp60
-rw-r--r--include/mbgl/actor/scheduler.hpp15
-rw-r--r--include/mbgl/annotation/annotation.hpp34
-rw-r--r--include/mbgl/map/map.hpp53
-rw-r--r--include/mbgl/map/mode.hpp23
-rw-r--r--include/mbgl/map/view.hpp18
-rw-r--r--include/mbgl/math/log2.hpp4
-rw-r--r--include/mbgl/renderer/backend_scope.hpp (renamed from include/mbgl/map/backend_scope.hpp)10
-rw-r--r--include/mbgl/renderer/mode.hpp18
-rw-r--r--include/mbgl/renderer/query.hpp (renamed from include/mbgl/map/query.hpp)10
-rw-r--r--include/mbgl/renderer/renderer.hpp58
-rw-r--r--include/mbgl/renderer/renderer_backend.hpp (renamed from include/mbgl/map/backend.hpp)45
-rw-r--r--include/mbgl/renderer/renderer_frontend.hpp31
-rw-r--r--include/mbgl/storage/default_file_source.hpp3
-rw-r--r--include/mbgl/storage/file_source.hpp6
-rw-r--r--include/mbgl/storage/offline.hpp6
-rw-r--r--include/mbgl/storage/online_file_source.hpp3
-rw-r--r--include/mbgl/storage/resource.hpp44
-rw-r--r--include/mbgl/storage/response.hpp11
-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.hpp64
-rw-r--r--include/mbgl/style/conversion/data_driven_property_value.hpp28
-rw-r--r--include/mbgl/style/conversion/expression.hpp39
-rw-r--r--include/mbgl/style/conversion/filter.hpp243
-rw-r--r--include/mbgl/style/conversion/function.hpp49
-rw-r--r--include/mbgl/style/conversion/geojson.hpp10
-rw-r--r--include/mbgl/style/conversion/geojson_options.hpp69
-rw-r--r--include/mbgl/style/conversion/get_json_type.hpp14
-rw-r--r--include/mbgl/style/conversion/layer.hpp210
-rw-r--r--include/mbgl/style/conversion/light.hpp106
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp207
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp.ejs48
-rw-r--r--include/mbgl/style/conversion/position.hpp14
-rw-r--r--include/mbgl/style/conversion/property_value.hpp19
-rw-r--r--include/mbgl/style/conversion/source.hpp176
-rw-r--r--include/mbgl/style/conversion/tileset.hpp65
-rw-r--r--include/mbgl/style/conversion/transition_options.hpp32
-rw-r--r--include/mbgl/style/expression/array_assertion.hpp39
-rw-r--r--include/mbgl/style/expression/assertion.hpp33
-rw-r--r--include/mbgl/style/expression/at.hpp38
-rw-r--r--include/mbgl/style/expression/boolean_operator.hpp49
-rw-r--r--include/mbgl/style/expression/case.hpp36
-rw-r--r--include/mbgl/style/expression/check_subtype.hpp17
-rw-r--r--include/mbgl/style/expression/coalesce.hpp45
-rw-r--r--include/mbgl/style/expression/coercion.hpp34
-rw-r--r--include/mbgl/style/expression/compound_expression.hpp138
-rw-r--r--include/mbgl/style/expression/expression.hpp173
-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.hpp177
-rw-r--r--include/mbgl/style/expression/is_constant.hpp35
-rw-r--r--include/mbgl/style/expression/is_expression.hpp13
-rw-r--r--include/mbgl/style/expression/let.hpp72
-rw-r--r--include/mbgl/style/expression/literal.hpp38
-rw-r--r--include/mbgl/style/expression/match.hpp45
-rw-r--r--include/mbgl/style/expression/parsing_context.hpp148
-rw-r--r--include/mbgl/style/expression/step.hpp45
-rw-r--r--include/mbgl/style/expression/type.hpp112
-rw-r--r--include/mbgl/style/expression/value.hpp153
-rw-r--r--include/mbgl/style/function/camera_function.hpp60
-rw-r--r--include/mbgl/style/function/composite_function.hpp138
-rw-r--r--include/mbgl/style/function/convert.hpp351
-rw-r--r--include/mbgl/style/function/source_function.hpp41
-rw-r--r--include/mbgl/style/layer.hpp4
-rw-r--r--include/mbgl/style/layers/circle_layer.hpp6
-rw-r--r--include/mbgl/style/layers/custom_layer.hpp16
-rw-r--r--include/mbgl/style/layers/line_layer.hpp6
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp32
-rw-r--r--include/mbgl/style/source.hpp4
-rw-r--r--include/mbgl/style/sources/custom_geometry_source.hpp56
-rw-r--r--include/mbgl/style/sources/geojson_source.hpp3
-rw-r--r--include/mbgl/style/sources/image_source.hpp2
-rw-r--r--include/mbgl/style/style.hpp6
-rw-r--r--include/mbgl/style/transition_options.hpp9
-rw-r--r--include/mbgl/style/types.hpp11
-rw-r--r--include/mbgl/tile/tile_id.hpp (renamed from src/mbgl/tile/tile_id.hpp)72
-rw-r--r--include/mbgl/tile/tile_necessity.hpp15
-rw-r--r--include/mbgl/util/any.hpp10
-rw-r--r--include/mbgl/util/constants.hpp7
-rw-r--r--include/mbgl/util/convert.hpp6
-rw-r--r--include/mbgl/util/enum.hpp1
-rw-r--r--include/mbgl/util/geo.hpp90
-rw-r--r--include/mbgl/util/geometry.hpp6
-rw-r--r--include/mbgl/util/image.hpp32
-rw-r--r--include/mbgl/util/indexed_tuple.hpp7
-rw-r--r--include/mbgl/util/interpolate.hpp39
-rw-r--r--include/mbgl/util/projection.hpp22
-rw-r--r--include/mbgl/util/run_loop.hpp12
-rw-r--r--include/mbgl/util/size.hpp4
-rw-r--r--include/mbgl/util/string.hpp33
-rw-r--r--include/mbgl/util/thread.hpp (renamed from src/mbgl/util/thread.hpp)2
-rw-r--r--include/mbgl/util/tileset.hpp15
-rw-r--r--include/mbgl/util/unique_any.hpp275
-rw-r--r--include/mbgl/util/unitbezier.hpp14
-rw-r--r--include/mbgl/util/util.hpp7
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)451
-rw-r--r--misc/cloudformation.template (renamed from cloudformation/travis.template)34
-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.json15
-rw-r--r--platform/android/.gitignore1
-rw-r--r--platform/android/CHANGELOG.md211
-rw-r--r--platform/android/MapboxGLAndroidSDK/.gitignore2
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle41
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle1
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle2
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties5
-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.pro8
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java5
-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.java39
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java52
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java106
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java48
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java51
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java60
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java63
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java20
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java19
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ILatLng.java18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/IProjectedMeters.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java153
-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.java46
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java45
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java33
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java111
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java306
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java95
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java84
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java17
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java512
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java538
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java628
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java185
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java48
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java365
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java32
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java38
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java38
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java38
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java60
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java73
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java80
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java142
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java29
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java293
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigException.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/GLSurfaceViewMapRenderer.java81
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java98
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java459
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java65
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java367
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java99
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java32
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java62
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java54
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnaphotUtil.java29
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java63
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java367
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java1761
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java127
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java1156
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java26
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs2
-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/GeoJsonOptions.java11
-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/text/LocalGlyphRasterizer.java46
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java87
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java58
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java27
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MapFragmentUtils.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-v21/mapbox_default_bg_selector.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mapview_preview.jpgbin166053 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.pngbin928 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_default_bg_selector.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml52
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-bg/strings.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-es/strings.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-hu/strings.xml13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-vi/strings.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties2
-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.java26
-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/AnnotationManagerTest.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java87
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java95
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java47
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java1010
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/.gitignore2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/build.gradle44
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/gradle-device-farm.gradle43
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/gradle-spoon.gradle8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml166
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml177
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java42
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java695
-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/MapboxMapAction.java49
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationChangeAction.java74
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java146
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerTest.java104
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerViewTest.java114
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolygonTest.java84
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolylineTest.java77
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java263
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java265
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java267
-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.java34
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/AttributionTest.java19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java112
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java44
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java256
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BaseStyleTest.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java1876
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java948
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java886
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java63
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java49
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java106
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java1871
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java528
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleBackgroundLayerTest.java61
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java128
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java5398
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs482
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs58
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java44
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml153
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java87
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java188
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java63
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java84
-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.java212
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java47
-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.java265
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java210
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java135
-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.java17
-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.java21
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java57
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java64
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java102
-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.java49
-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/SupportMapFragmentActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java5
-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.java24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java64
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java281
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java215
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java80
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java (renamed from platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/SimpleWearMapActivity.java)31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java102
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java35
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java21
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java252
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java142
-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.java58
-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.java126
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java79
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java106
-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.java29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java38
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java97
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java321
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java102
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java16
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java151
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java235
-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.java112
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java309
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java72
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java202
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java145
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java265
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java98
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java49
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java55
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java125
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java33
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java137
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java86
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java153
-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/annotations/PulseMarkerViewOptions.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java1
-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.java42
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java21
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java74
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/MapViewPager.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_white.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_check_box.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml28
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_bottom_sheet.xml76
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml2
-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_debug_mode.xml121
-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_latlngbounds.xml36
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml15
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_marker.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_reuse.xml24
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_visibility.xml (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml)10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml25
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml33
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml41
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml (renamed from platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.xml)8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml98
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml29
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml30
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml7
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_animator.xml20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.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_press_for_marker.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml32
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml10
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson2741
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml102
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml72
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml176
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml72
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/build.gradle50
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/gradle-checkstyle.gradle17
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/gradle-config.gradle22
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/proguard-rules.pro17
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/AndroidManifest.xml47
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java28
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/FeatureOverviewActivity.java47
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/adapter/FeatureAdapter.java78
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/model/Feature.java30
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/utils/OffsettingHelper.java40
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_feature_overview.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml22
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/item_curved_layout.xml19
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-hdpi/ic_launcher.pngbin19772 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-mdpi/ic_launcher.pngbin11003 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xhdpi/ic_launcher.pngbin30669 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin58564 -> 0 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/colors.xml31
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/strings.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKWearTestApp/src/test/java/com/mapbox/weartestapp/utils/OffsettingHelperTest.java28
-rw-r--r--platform/android/README.md101
-rw-r--r--platform/android/build.gradle6
-rw-r--r--platform/android/config.cmake126
-rw-r--r--platform/android/dependencies.gradle33
-rw-r--r--platform/android/gradle-lint.gradle30
-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.json31
-rw-r--r--platform/android/scripts/generate-style-code.js10
-rw-r--r--platform/android/scripts/generate-test-code.js23
-rw-r--r--platform/android/settings.gradle4
-rwxr-xr-xplatform/android/src/android_renderer_backend.cpp63
-rwxr-xr-xplatform/android/src/android_renderer_backend.hpp41
-rw-r--r--platform/android/src/android_renderer_frontend.cpp122
-rw-r--r--platform/android/src/android_renderer_frontend.hpp50
-rw-r--r--platform/android/src/example_custom_layer.cpp8
-rw-r--r--platform/android/src/file_source.cpp29
-rw-r--r--platform/android/src/file_source.hpp5
-rw-r--r--platform/android/src/geometry/lat_lng_bounds.cpp8
-rw-r--r--platform/android/src/http_file_source.cpp4
-rwxr-xr-xplatform/android/src/jni.cpp19
-rw-r--r--platform/android/src/jni/collection.hpp26
-rw-r--r--platform/android/src/jni/generic_global_ref_deleter.hpp29
-rw-r--r--platform/android/src/map/camera_position.cpp18
-rw-r--r--platform/android/src/map/camera_position.hpp2
-rw-r--r--platform/android/src/map/image.cpp44
-rw-r--r--platform/android/src/map/image.hpp26
-rw-r--r--platform/android/src/map_renderer.cpp206
-rw-r--r--platform/android/src/map_renderer.hpp128
-rw-r--r--platform/android/src/map_renderer_runnable.cpp51
-rw-r--r--platform/android/src/map_renderer_runnable.hpp49
-rwxr-xr-xplatform/android/src/native_map_view.cpp736
-rwxr-xr-xplatform/android/src/native_map_view.hpp123
-rw-r--r--platform/android/src/run_loop.cpp17
-rw-r--r--platform/android/src/snapshotter/map_snapshot.cpp60
-rw-r--r--platform/android/src/snapshotter/map_snapshot.hpp46
-rw-r--r--platform/android/src/snapshotter/map_snapshotter.cpp155
-rw-r--r--platform/android/src/snapshotter/map_snapshotter.hpp76
-rw-r--r--platform/android/src/style/android_conversion.hpp147
-rw-r--r--platform/android/src/style/conversion/filter.hpp3
-rw-r--r--platform/android/src/style/conversion/geojson.hpp24
-rw-r--r--platform/android/src/style/conversion/types.hpp8
-rw-r--r--platform/android/src/style/conversion/types_string_values.hpp56
-rw-r--r--platform/android/src/style/conversion/url_or_tileset.hpp9
-rw-r--r--platform/android/src/style/layers/circle_layer.cpp7
-rw-r--r--platform/android/src/style/layers/circle_layer.hpp2
-rw-r--r--platform/android/src/style/layers/custom_layer.cpp5
-rw-r--r--platform/android/src/style/layers/custom_layer.hpp2
-rw-r--r--platform/android/src/style/layers/layer.cpp21
-rw-r--r--platform/android/src/style/layers/symbol_layer.cpp14
-rw-r--r--platform/android/src/style/layers/symbol_layer.hpp4
-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.cpp26
-rw-r--r--platform/android/src/style/sources/geojson_source.hpp5
-rw-r--r--platform/android/src/style/sources/image_source.cpp13
-rw-r--r--platform/android/src/style/sources/image_source.hpp5
-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.cpp114
-rw-r--r--platform/android/src/style/sources/source.hpp34
-rw-r--r--platform/android/src/style/sources/sources.cpp53
-rw-r--r--platform/android/src/style/sources/sources.hpp20
-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.cpp16
-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/android/src/text/local_glyph_rasterizer.cpp126
-rw-r--r--platform/android/src/text/local_glyph_rasterizer_jni.hpp31
-rw-r--r--platform/android/tests/docs/UI_TESTS.md2
-rw-r--r--platform/android/tests/docs/UNIT_TESTS.md2
-rw-r--r--platform/darwin/docs/guides/For Style Authors.md.ejs2
-rw-r--r--platform/darwin/docs/guides/Working with GeoJSON Data.md6
-rw-r--r--platform/darwin/docs/theme/assets/css/jazzy.css.scss1
-rw-r--r--platform/darwin/mbgl/gl/gl_impl.hpp16
-rw-r--r--platform/darwin/mbgl/util/image+MGLAdditions.hpp2
-rw-r--r--platform/darwin/resources/bg.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/bg.lproj/Foundation.stringsdict54
-rw-r--r--platform/darwin/resources/ca.lproj/Foundation.strings4
-rw-r--r--platform/darwin/resources/de.lproj/Foundation.stringsdict16
-rw-r--r--platform/darwin/resources/fr.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/hu.lproj/Foundation.stringsdict54
-rw-r--r--platform/darwin/resources/lt.lproj/Foundation.stringsdict6
-rw-r--r--platform/darwin/resources/pt-BR.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/ru.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/uk.lproj/Foundation.strings291
-rw-r--r--platform/darwin/resources/uk.lproj/Foundation.stringsdict16
-rw-r--r--platform/darwin/resources/vi.lproj/Foundation.stringsdict48
-rw-r--r--platform/darwin/resources/zh-Hant.lproj/Foundation.strings42
-rw-r--r--platform/darwin/scripts/generate-style-code.js53
-rw-r--r--platform/darwin/scripts/style-spec-overrides-v8.json6
-rw-r--r--platform/darwin/src/CFHandle.hpp31
-rw-r--r--platform/darwin/src/MGLAbstractShapeSource.h99
-rw-r--r--platform/darwin/src/MGLAbstractShapeSource.mm117
-rw-r--r--platform/darwin/src/MGLAbstractShapeSource_Private.h22
-rw-r--r--platform/darwin/src/MGLAccountManager.m4
-rw-r--r--platform/darwin/src/MGLAttributionInfo.mm1
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.h45
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.mm32
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.h113
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.mm169
-rw-r--r--platform/darwin/src/MGLConversion.h223
-rw-r--r--platform/darwin/src/MGLFeature.h14
-rw-r--r--platform/darwin/src/MGLFeature.mm55
-rw-r--r--platform/darwin/src/MGLForegroundStyleLayer.h7
-rw-r--r--platform/darwin/src/MGLGeometry.h4
-rw-r--r--platform/darwin/src/MGLGeometry.mm49
-rw-r--r--platform/darwin/src/MGLGeometry_Private.h42
-rw-r--r--platform/darwin/src/MGLImageSource.h5
-rw-r--r--platform/darwin/src/MGLImageSource.mm5
-rw-r--r--platform/darwin/src/MGLLight.h120
-rw-r--r--platform/darwin/src/MGLLight.h.ejs100
-rw-r--r--platform/darwin/src/MGLLight.mm41
-rw-r--r--platform/darwin/src/MGLLight.mm.ejs114
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.h14
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.mm6
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.h206
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm421
-rw-r--r--platform/darwin/src/MGLMultiPoint.h5
-rw-r--r--platform/darwin/src/MGLMultiPoint.mm2
-rw-r--r--platform/darwin/src/MGLNetworkConfiguration.m2
-rw-r--r--platform/darwin/src/MGLOfflinePack.h2
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm3
-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.mm36
-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.h14
-rw-r--r--platform/darwin/src/MGLPolyline.mm23
-rw-r--r--platform/darwin/src/MGLPolyline_Private.h12
-rw-r--r--platform/darwin/src/MGLRendererConfiguration.h40
-rw-r--r--platform/darwin/src/MGLRendererConfiguration.mm43
-rw-r--r--platform/darwin/src/MGLRendererFrontend.h70
-rw-r--r--platform/darwin/src/MGLShape.h10
-rw-r--r--platform/darwin/src/MGLShapeSource.h73
-rw-r--r--platform/darwin/src/MGLShapeSource.mm67
-rw-r--r--platform/darwin/src/MGLShapeSource_Private.h3
-rw-r--r--platform/darwin/src/MGLSource.h8
-rw-r--r--platform/darwin/src/MGLSource.mm27
-rw-r--r--platform/darwin/src/MGLSource_Private.h25
-rw-r--r--platform/darwin/src/MGLStyle.h61
-rw-r--r--platform/darwin/src/MGLStyle.mm211
-rw-r--r--platform/darwin/src/MGLStyleLayer.h8
-rw-r--r--platform/darwin/src/MGLStyleValue.h9
-rw-r--r--platform/darwin/src/MGLStyleValue_Private.h4
-rw-r--r--platform/darwin/src/MGLStyle_Private.h9
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h184
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm96
-rw-r--r--platform/darwin/src/MGLTileSource.h6
-rw-r--r--platform/darwin/src/MGLTypes.h4
-rw-r--r--platform/darwin/src/MGLVectorSource.mm58
-rw-r--r--platform/darwin/src/MGLVectorSource_Private.h7
-rw-r--r--platform/darwin/src/MGLVectorStyleLayer.h9
-rw-r--r--platform/darwin/src/NSBundle+MGLAdditions.m2
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.m11
-rw-r--r--platform/darwin/src/headless_backend_cgl.cpp132
-rw-r--r--platform/darwin/src/headless_backend_eagl.mm61
-rw-r--r--platform/darwin/src/headless_display_cgl.cpp60
-rw-r--r--platform/darwin/src/http_file_source.mm4
-rw-r--r--platform/darwin/src/image.mm16
-rw-r--r--platform/darwin/src/local_glyph_rasterizer.mm175
-rw-r--r--platform/darwin/src/nsthread.mm3
-rw-r--r--platform/darwin/src/run_loop.cpp26
-rw-r--r--platform/darwin/test/MGLAttributionInfoTests.m4
-rw-r--r--platform/darwin/test/MGLCircleStyleLayerTests.mm42
-rw-r--r--platform/darwin/test/MGLComputedShapeSourceTests.m24
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift30
-rw-r--r--platform/darwin/test/MGLLightTest.mm177
-rw-r--r--platform/darwin/test/MGLLightTest.mm.ejs92
-rw-r--r--platform/darwin/test/MGLLineStyleLayerTests.mm7
-rw-r--r--platform/darwin/test/MGLNSStringAdditionsTests.m2
-rw-r--r--platform/darwin/test/MGLSDKTestHelpers.swift3
-rw-r--r--platform/darwin/test/MGLShapeSourceTests.mm1
-rw-r--r--platform/darwin/test/MGLStyleTests.mm5
-rw-r--r--platform/darwin/test/MGLStyleValueTests.swift97
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm165
-rw-r--r--platform/default/asset_file_source.cpp3
-rw-r--r--platform/default/default_file_source.cpp89
-rw-r--r--platform/default/file_source_request.cpp (renamed from src/mbgl/storage/file_source_request.cpp)4
-rw-r--r--platform/default/headless_backend_osmesa.cpp44
-rw-r--r--platform/default/http_file_source.cpp4
-rw-r--r--platform/default/local_glyph_rasterizer.cpp22
-rw-r--r--platform/default/mbgl/gl/headless_backend.cpp64
-rw-r--r--platform/default/mbgl/gl/headless_backend.hpp32
-rw-r--r--platform/default/mbgl/gl/headless_display.cpp15
-rw-r--r--platform/default/mbgl/gl/headless_display.hpp20
-rw-r--r--platform/default/mbgl/gl/headless_frontend.cpp96
-rw-r--r--platform/default/mbgl/gl/headless_frontend.hpp52
-rw-r--r--platform/default/mbgl/gl/offscreen_view.cpp65
-rw-r--r--platform/default/mbgl/gl/offscreen_view.hpp28
-rw-r--r--platform/default/mbgl/map/map_snapshotter.cpp184
-rw-r--r--platform/default/mbgl/map/map_snapshotter.hpp61
-rw-r--r--platform/default/mbgl/storage/file_source_request.hpp (renamed from src/mbgl/storage/file_source_request.hpp)0
-rw-r--r--platform/default/mbgl/storage/offline.cpp35
-rw-r--r--platform/default/mbgl/storage/offline_database.cpp197
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp1
-rw-r--r--platform/default/mbgl/storage/offline_download.cpp6
-rw-r--r--platform/default/mbgl/storage/offline_download.hpp2
-rw-r--r--platform/default/mbgl/storage/offline_schema.cpp.include2
-rw-r--r--platform/default/mbgl/storage/offline_schema.sql2
-rw-r--r--platform/default/mbgl/util/default_styles.cpp18
-rw-r--r--platform/default/mbgl/util/default_styles.hpp16
-rw-r--r--platform/default/online_file_source.cpp41
-rw-r--r--platform/default/resources/default_marker.svg2
-rw-r--r--platform/default/run_loop.cpp19
-rw-r--r--platform/default/sqlite3.cpp9
-rw-r--r--platform/default/thread_local.cpp66
-rw-r--r--platform/default/utf.cpp10
-rw-r--r--platform/glfw/glfw_renderer_frontend.cpp41
-rw-r--r--platform/glfw/glfw_renderer_frontend.hpp29
-rw-r--r--platform/glfw/glfw_view.cpp23
-rw-r--r--platform/glfw/glfw_view.hpp26
-rw-r--r--platform/glfw/main.cpp148
-rw-r--r--platform/glfw/settings_json.cpp3
-rw-r--r--platform/glfw/settings_json.hpp4
-rw-r--r--platform/ios/CHANGELOG.md103
-rw-r--r--platform/ios/DEVELOPING.md44
-rw-r--r--platform/ios/INSTALL.md20
-rw-r--r--platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec2
-rw-r--r--platform/ios/Mapbox-iOS-SDK-symbols.podspec2
-rw-r--r--platform/ios/Mapbox-iOS-SDK.podspec2
-rw-r--r--platform/ios/README.md9
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json6
-rw-r--r--platform/ios/app/Assets.xcassets/AppIcon.appiconset/Icon-1024.pngbin0 -> 12225 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/Contents.json15
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/settings.pdfbin0 -> 9177 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/settings.pngbin528 -> 0 bytes
-rw-r--r--platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.pngbin1130 -> 0 bytes
-rw-r--r--platform/ios/app/Default-568h@2x.pngbin18594 -> 0 bytes
-rw-r--r--platform/ios/app/Info.plist21
-rw-r--r--platform/ios/app/LaunchScreen.storyboard14
-rw-r--r--platform/ios/app/MBXAppDelegate.m18
-rw-r--r--platform/ios/app/MBXSnapshotsViewController.h5
-rw-r--r--platform/ios/app/MBXSnapshotsViewController.m66
-rw-r--r--platform/ios/app/MBXViewController.m316
-rw-r--r--platform/ios/app/Main.storyboard110
-rw-r--r--platform/ios/app/bg.lproj/Localizable.strings0
-rw-r--r--platform/ios/app/hu.lproj/Localizable.strings0
-rw-r--r--platform/ios/benchmark/Info.plist2
-rw-r--r--platform/ios/bitrise.yml76
-rw-r--r--platform/ios/config.cmake86
-rw-r--r--platform/ios/docs/doc-README.md10
-rw-r--r--platform/ios/docs/guides/Adding Points to a Map.md4
-rw-r--r--platform/ios/docs/guides/For Style Authors.md2
-rw-r--r--platform/ios/docs/guides/Gesture Recognizers.md2
-rw-r--r--platform/ios/docs/guides/Info.plist Keys.md6
-rw-r--r--platform/ios/docs/pod-README.md14
-rw-r--r--platform/ios/framework/Settings.bundle/bg.lproj/Root.strings3
-rw-r--r--platform/ios/framework/Settings.bundle/hu.lproj/Root.strings3
-rw-r--r--platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings4
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj321
-rw-r--r--platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme4
-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.yml4
-rw-r--r--platform/ios/resources/Base.lproj/Localizable.strings29
-rw-r--r--platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdfbin2601 -> 4354 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-digicert_2016.der (renamed from platform/ios/resources/api_mapbox_com-digicert.der)bin1913 -> 1913 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-digicert_2017.derbin0 -> 2030 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-geotrust_2016.der (renamed from platform/ios/resources/api_mapbox_com-geotrust.der)bin1757 -> 1757 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-geotrust_2017.derbin0 -> 1758 bytes
-rw-r--r--platform/ios/resources/bg.lproj/Localizable.strings93
-rw-r--r--platform/ios/resources/bg.lproj/Localizable.stringsdict34
-rw-r--r--platform/ios/resources/ca.lproj/Localizable.strings6
-rw-r--r--platform/ios/resources/de.lproj/Localizable.strings22
-rw-r--r--platform/ios/resources/en.lproj/Localizable.stringsdict38
-rw-r--r--platform/ios/resources/es.lproj/Localizable.strings24
-rw-r--r--platform/ios/resources/fr.lproj/Localizable.strings22
-rw-r--r--platform/ios/resources/hu.lproj/Localizable.strings93
-rw-r--r--platform/ios/resources/ja.lproj/Localizable.strings6
-rw-r--r--platform/ios/resources/lt.lproj/Localizable.strings6
-rw-r--r--platform/ios/resources/lt.lproj/Localizable.stringsdict12
-rw-r--r--platform/ios/resources/pt-BR.lproj/Localizable.strings30
-rw-r--r--platform/ios/resources/ru.lproj/Localizable.strings24
-rw-r--r--platform/ios/resources/sv.lproj/Localizable.strings12
-rw-r--r--platform/ios/resources/uk.lproj/Localizable.strings30
-rw-r--r--platform/ios/resources/uk.lproj/Localizable.stringsdict38
-rw-r--r--platform/ios/resources/vi.lproj/Localizable.strings6
-rw-r--r--platform/ios/resources/zh-Hans.lproj/Localizable.strings6
-rw-r--r--platform/ios/resources/zh-Hant.lproj/Localizable.strings39
-rw-r--r--platform/ios/resources/zh-Hant.lproj/Localizable.stringsdict30
-rwxr-xr-xplatform/ios/scripts/deploy-packages.sh21
-rwxr-xr-xplatform/ios/scripts/document.sh2
-rwxr-xr-xplatform/ios/scripts/package.sh40
-rwxr-xr-xplatform/ios/scripts/release-fabric.sh37
-rwxr-xr-xplatform/ios/scripts/validate-framework-zip.sh (renamed from platform/ios/scripts/validate-fabric-zip.sh)0
-rw-r--r--platform/ios/src/MGLAPIClient.m114
-rw-r--r--platform/ios/src/MGLAnnotationImage.h3
-rw-r--r--platform/ios/src/MGLAnnotationView.h3
-rw-r--r--platform/ios/src/MGLCompactCalloutView.h2
-rw-r--r--platform/ios/src/MGLFaux3DUserLocationAnnotationView.h10
-rw-r--r--platform/ios/src/MGLFaux3DUserLocationAnnotationView.m197
-rw-r--r--platform/ios/src/MGLMapAccessibilityElement.h54
-rw-r--r--platform/ios/src/MGLMapAccessibilityElement.mm200
-rw-r--r--platform/ios/src/MGLMapView+IBAdditions.h1
-rw-r--r--platform/ios/src/MGLMapView.h113
-rw-r--r--platform/ios/src/MGLMapView.mm1416
-rw-r--r--platform/ios/src/MGLMapView_Private.h3
-rw-r--r--platform/ios/src/MGLSDKUpdateChecker.mm2
-rw-r--r--platform/ios/src/MGLScaleBar.mm22
-rw-r--r--platform/ios/src/MGLUserLocation.h10
-rw-r--r--platform/ios/src/MGLUserLocation.m3
-rw-r--r--platform/ios/src/MGLUserLocationAnnotationView.h2
-rw-r--r--platform/ios/src/MGLUserLocationHeadingArrowLayer.h11
-rw-r--r--platform/ios/src/MGLUserLocationHeadingArrowLayer.m59
-rw-r--r--platform/ios/src/MGLUserLocationHeadingBeamLayer.h11
-rw-r--r--platform/ios/src/MGLUserLocationHeadingBeamLayer.m104
-rw-r--r--platform/ios/src/MGLUserLocationHeadingIndicator.h10
-rw-r--r--platform/ios/src/Mapbox.h3
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.h2
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.mm15
-rw-r--r--platform/ios/test/MGLMapAccessibilityElementTests.m87
m---------platform/ios/uitest/KIF0
-rw-r--r--platform/ios/uitest/LaunchScreen.xib2
m---------platform/ios/uitest/OHHTTPStubs0
-rw-r--r--platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme4
m---------platform/ios/vendor/SMCalloutView0
-rwxr-xr-xplatform/ios/vendor/SMCalloutView/SMCalloutView.h205
-rwxr-xr-xplatform/ios/vendor/SMCalloutView/SMCalloutView.m851
-rw-r--r--platform/linux/README.md9
-rw-r--r--platform/linux/config.cmake76
-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.cpp65
-rw-r--r--platform/linux/src/headless_display_glx.cpp78
-rw-r--r--platform/macos/CHANGELOG.md81
-rw-r--r--platform/macos/DEVELOPING.md32
-rw-r--r--platform/macos/INSTALL.md8
-rw-r--r--platform/macos/Mapbox-macOS-SDK-symbols.podspec2
-rw-r--r--platform/macos/Mapbox-macOS-SDK.podspec2
-rw-r--r--platform/macos/app/Base.lproj/MainMenu.xib26
-rw-r--r--platform/macos/app/Base.lproj/MapDocument.xib6
-rw-r--r--platform/macos/app/Info.plist2
-rw-r--r--platform/macos/app/MGLVectorSource+MBXAdditions.h15
-rw-r--r--platform/macos/app/MGLVectorSource+MBXAdditions.m53
-rw-r--r--platform/macos/app/MapDocument.m217
-rw-r--r--platform/macos/app/bg.lproj/Localizable.strings0
-rw-r--r--platform/macos/app/hu.lproj/Localizable.strings0
-rw-r--r--platform/macos/bitrise.yml34
-rw-r--r--platform/macos/config.cmake83
-rw-r--r--platform/macos/docs/guides/Info.plist Keys.md6
-rw-r--r--platform/macos/docs/img/screenshot.jpgbin445665 -> 351938 bytes
-rw-r--r--platform/macos/jazzy.yml4
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj158
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme8
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme4
-rw-r--r--platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme4
-rwxr-xr-xplatform/macos/scripts/create_scheme.sh40
-rw-r--r--platform/macos/sdk/Info.plist6
-rw-r--r--platform/macos/sdk/bg.lproj/Localizable.strings27
-rw-r--r--platform/macos/sdk/ca.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/default_marker.pdfbin2601 -> 4354 bytes
-rw-r--r--platform/macos/sdk/es.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/fr.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/hu.lproj/Localizable.strings27
-rw-r--r--platform/macos/sdk/pt-BR.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/ru.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/uk.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/vi.lproj/Localizable.strings14
-rw-r--r--platform/macos/sdk/zh-Hant.lproj/Localizable.strings12
-rw-r--r--platform/macos/src/MGLMapView.h40
-rw-r--r--platform/macos/src/MGLMapView.mm145
-rw-r--r--platform/macos/src/MGLMapView_Private.h3
-rw-r--r--platform/macos/src/Mapbox.h3
-rw-r--r--platform/macos/src/NSImage+MGLAdditions.mm17
-rw-r--r--platform/node/CHANGELOG.md26
-rw-r--r--platform/node/DEVELOPING.md2
-rw-r--r--platform/node/bitrise.yml69
-rw-r--r--platform/node/index.js27
-rwxr-xr-xplatform/node/scripts/after_script.sh14
-rwxr-xr-xplatform/node/scripts/after_success.sh11
-rw-r--r--platform/node/src/node_conversion.hpp183
-rw-r--r--platform/node/src/node_expression.cpp230
-rw-r--r--platform/node/src/node_expression.hpp40
-rw-r--r--platform/node/src/node_geojson.hpp15
-rw-r--r--platform/node/src/node_map.cpp244
-rw-r--r--platform/node/src/node_map.hpp23
-rw-r--r--platform/node/src/node_mapbox_gl_native.cpp2
-rw-r--r--platform/node/src/node_request.cpp10
-rw-r--r--platform/node/src/node_request.hpp6
-rw-r--r--platform/node/test/expression.test.js71
-rw-r--r--platform/node/test/ignores.json153
-rw-r--r--platform/node/test/js/map.test.js4
-rw-r--r--platform/node/test/js/request.test.js167
-rw-r--r--platform/node/test/js/request_fail.test.js59
-rw-r--r--platform/node/test/js/request_notfound.test.js74
-rw-r--r--platform/node/test/mockfs.js6
-rw-r--r--platform/node/test/query.test.js9
-rw-r--r--platform/node/test/render.test.js13
-rw-r--r--platform/node/test/suite_implementation.js74
-rw-r--r--platform/qt/app/mapwindow.cpp22
-rw-r--r--platform/qt/bitrise-qt4.yml36
-rw-r--r--platform/qt/bitrise-qt5.yml47
-rw-r--r--platform/qt/config.cmake35
-rw-r--r--platform/qt/include/qmapbox.hpp33
-rw-r--r--platform/qt/include/qmapboxgl.hpp6
-rw-r--r--platform/qt/mbgl/gl/gl_impl.hpp178
-rw-r--r--platform/qt/qt.cmake103
-rw-r--r--platform/qt/qt4.cmake25
-rw-r--r--platform/qt/qt5.cmake32
-rw-r--r--platform/qt/resources/common.qrc2
-rw-r--r--platform/qt/src/headless_backend_qt.cpp (renamed from platform/qt/test/headless_backend_qt.cpp)36
-rw-r--r--platform/qt/src/http_file_source.cpp3
-rw-r--r--platform/qt/src/http_request.cpp16
-rw-r--r--platform/qt/src/http_request.hpp2
-rw-r--r--platform/qt/src/qmapbox.cpp2
-rw-r--r--platform/qt/src/qmapboxgl.cpp177
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp27
-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/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.cpp (renamed from platform/qt/src/image.cpp)5
-rwxr-xr-xplatform/qt/src/qt_logging.cpp12
-rw-r--r--platform/qt/src/run_loop.cpp22
-rw-r--r--platform/qt/src/sqlite3.cpp104
-rw-r--r--platform/qt/src/thread.cpp19
-rw-r--r--platform/qt/src/thread_local.cpp49
-rw-r--r--platform/qt/test/qmapboxgl.cpp98
-rw-r--r--platform/qt/test/qmapboxgl.test.cpp80
-rw-r--r--platform/qt/test/qmapboxgl.test.hpp36
-rwxr-xr-xscripts/check-cxx11abi.sh4
-rwxr-xr-xscripts/circle_setup.sh35
-rwxr-xr-xscripts/clang-tools.sh4
-rw-r--r--scripts/config.xcconfig.in5
-rwxr-xr-xscripts/generate-shaders.js92
-rw-r--r--scripts/generate-style-code.js7
-rw-r--r--scripts/launch-c-xcode.in4
-rw-r--r--scripts/launch-c.in4
-rw-r--r--scripts/launch-cxx-xcode.in4
-rw-r--r--scripts/launch-cxx.in4
-rw-r--r--scripts/lldb-types15
-rwxr-xr-xscripts/update_ca_bundle.sh2
-rw-r--r--scripts/valgrind.sup300
-rw-r--r--src/mbgl/actor/scheduler.cpp19
-rw-r--r--src/mbgl/algorithm/generate_clip_ids.cpp16
-rw-r--r--src/mbgl/algorithm/generate_clip_ids.hpp14
-rw-r--r--src/mbgl/algorithm/generate_clip_ids_impl.hpp28
-rw-r--r--src/mbgl/algorithm/update_renderables.hpp34
-rw-r--r--src/mbgl/algorithm/update_tile_masks.hpp128
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp135
-rw-r--r--src/mbgl/annotation/annotation_manager.hpp40
-rw-r--r--src/mbgl/annotation/annotation_tile.cpp3
-rw-r--r--src/mbgl/annotation/annotation_tile.hpp2
-rw-r--r--src/mbgl/annotation/fill_annotation_impl.cpp6
-rw-r--r--src/mbgl/annotation/fill_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/line_annotation_impl.cpp6
-rw-r--r--src/mbgl/annotation/line_annotation_impl.hpp2
-rw-r--r--src/mbgl/annotation/render_annotation_source.cpp29
-rw-r--r--src/mbgl/annotation/render_annotation_source.hpp14
-rw-r--r--src/mbgl/annotation/shape_annotation_impl.cpp10
-rw-r--r--src/mbgl/annotation/shape_annotation_impl.hpp4
-rw-r--r--src/mbgl/annotation/symbol_annotation_impl.hpp2
-rw-r--r--src/mbgl/geometry/feature_index.cpp123
-rw-r--r--src/mbgl/geometry/feature_index.hpp42
-rw-r--r--src/mbgl/gl/attribute.cpp71
-rw-r--r--src/mbgl/gl/attribute.hpp163
-rw-r--r--src/mbgl/gl/color_mode.hpp2
-rw-r--r--src/mbgl/gl/context.cpp161
-rw-r--r--src/mbgl/gl/context.hpp57
-rw-r--r--src/mbgl/gl/gl.cpp3
-rw-r--r--src/mbgl/gl/gl.hpp30
-rw-r--r--src/mbgl/gl/index_buffer.hpp2
-rw-r--r--src/mbgl/gl/object.cpp4
-rw-r--r--src/mbgl/gl/program.hpp56
-rw-r--r--src/mbgl/gl/renderbuffer.hpp16
-rw-r--r--src/mbgl/gl/segment.cpp7
-rw-r--r--src/mbgl/gl/segment.hpp73
-rw-r--r--src/mbgl/gl/texture.hpp20
-rw-r--r--src/mbgl/gl/types.hpp18
-rw-r--r--src/mbgl/gl/uniform.hpp6
-rw-r--r--src/mbgl/gl/value.cpp24
-rw-r--r--src/mbgl/gl/value.hpp9
-rw-r--r--src/mbgl/gl/vertex_array.cpp18
-rw-r--r--src/mbgl/gl/vertex_array.hpp73
-rw-r--r--src/mbgl/gl/vertex_buffer.hpp1
-rw-r--r--src/mbgl/layout/symbol_instance.cpp44
-rw-r--r--src/mbgl/layout/symbol_instance.hpp25
-rw-r--r--src/mbgl/layout/symbol_layout.cpp374
-rw-r--r--src/mbgl/layout/symbol_layout.hpp37
-rw-r--r--src/mbgl/layout/symbol_projection.cpp420
-rw-r--r--src/mbgl/layout/symbol_projection.hpp63
-rw-r--r--src/mbgl/map/backend_scope.cpp47
-rw-r--r--src/mbgl/map/map.cpp524
-rw-r--r--src/mbgl/map/transform.cpp34
-rw-r--r--src/mbgl/map/transform.hpp8
-rw-r--r--src/mbgl/map/transform_state.cpp25
-rw-r--r--src/mbgl/map/transform_state.hpp9
-rw-r--r--src/mbgl/map/update.hpp26
-rw-r--r--src/mbgl/map/zoom_history.hpp40
-rw-r--r--src/mbgl/programs/attributes.hpp9
-rw-r--r--src/mbgl/programs/binary_program.cpp5
-rw-r--r--src/mbgl/programs/binary_program.hpp4
-rw-r--r--src/mbgl/programs/circle_program.hpp4
-rw-r--r--src/mbgl/programs/collision_box_program.cpp2
-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/line_program.cpp2
-rw-r--r--src/mbgl/programs/line_program.hpp13
-rw-r--r--src/mbgl/programs/program.hpp47
-rw-r--r--src/mbgl/programs/programs.hpp4
-rw-r--r--src/mbgl/programs/segment.hpp43
-rw-r--r--src/mbgl/programs/symbol_program.cpp60
-rw-r--r--src/mbgl/programs/symbol_program.hpp338
-rw-r--r--src/mbgl/programs/uniforms.hpp5
-rw-r--r--src/mbgl/renderer/backend_scope.cpp66
-rw-r--r--src/mbgl/renderer/bucket.hpp21
-rw-r--r--src/mbgl/renderer/buckets/circle_bucket.cpp10
-rw-r--r--src/mbgl/renderer/buckets/circle_bucket.hpp6
-rw-r--r--src/mbgl/renderer/buckets/debug_bucket.cpp2
-rw-r--r--src/mbgl/renderer/buckets/debug_bucket.hpp2
-rw-r--r--src/mbgl/renderer/buckets/fill_bucket.cpp8
-rw-r--r--src/mbgl/renderer/buckets/fill_bucket.hpp7
-rw-r--r--src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp14
-rw-r--r--src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp5
-rw-r--r--src/mbgl/renderer/buckets/line_bucket.cpp28
-rw-r--r--src/mbgl/renderer/buckets/line_bucket.hpp8
-rw-r--r--src/mbgl/renderer/buckets/raster_bucket.cpp89
-rw-r--r--src/mbgl/renderer/buckets/raster_bucket.hpp17
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.cpp178
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp79
-rw-r--r--src/mbgl/renderer/cross_faded_property_evaluator.cpp5
-rw-r--r--src/mbgl/renderer/frame_history.cpp77
-rw-r--r--src/mbgl/renderer/frame_history.hpp40
-rw-r--r--src/mbgl/renderer/image_manager.cpp42
-rw-r--r--src/mbgl/renderer/image_manager.hpp15
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp78
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.hpp1
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp53
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.hpp1
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.cpp27
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.hpp7
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp116
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp5
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp134
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.hpp1
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp85
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.hpp1
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.cpp127
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.hpp2
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp260
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.hpp4
-rw-r--r--src/mbgl/renderer/paint_parameters.cpp96
-rw-r--r--src/mbgl/renderer/paint_parameters.hpp68
-rw-r--r--src/mbgl/renderer/paint_property_binder.hpp26
-rw-r--r--src/mbgl/renderer/painter.cpp387
-rw-r--r--src/mbgl/renderer/painter.hpp181
-rw-r--r--src/mbgl/renderer/painters/painter_background.cpp83
-rw-r--r--src/mbgl/renderer/painters/painter_circle.cpp57
-rw-r--r--src/mbgl/renderer/painters/painter_clipping.cpp37
-rw-r--r--src/mbgl/renderer/painters/painter_debug.cpp162
-rw-r--r--src/mbgl/renderer/painters/painter_fill.cpp141
-rw-r--r--src/mbgl/renderer/painters/painter_fill_extrusion.cpp86
-rw-r--r--src/mbgl/renderer/painters/painter_line.cpp91
-rw-r--r--src/mbgl/renderer/painters/painter_raster.cpp89
-rw-r--r--src/mbgl/renderer/painters/painter_symbol.cpp166
-rw-r--r--src/mbgl/renderer/possibly_evaluated_property_value.hpp2
-rw-r--r--src/mbgl/renderer/render_item.hpp36
-rw-r--r--src/mbgl/renderer/render_layer.cpp10
-rw-r--r--src/mbgl/renderer/render_layer.hpp14
-rw-r--r--src/mbgl/renderer/render_pass.hpp8
-rw-r--r--src/mbgl/renderer/render_source.cpp3
-rw-r--r--src/mbgl/renderer/render_source.hpp17
-rw-r--r--src/mbgl/renderer/render_static_data.cpp67
-rw-r--r--src/mbgl/renderer/render_static_data.hpp39
-rw-r--r--src/mbgl/renderer/render_style.cpp456
-rw-r--r--src/mbgl/renderer/render_style.hpp93
-rw-r--r--src/mbgl/renderer/render_style_observer.hpp14
-rw-r--r--src/mbgl/renderer/render_tile.cpp127
-rw-r--r--src/mbgl/renderer/render_tile.hpp14
-rw-r--r--src/mbgl/renderer/renderer.cpp102
-rw-r--r--src/mbgl/renderer/renderer_backend.cpp (renamed from src/mbgl/map/backend.cpp)28
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp815
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp121
-rw-r--r--src/mbgl/renderer/renderer_observer.hpp35
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.cpp86
-rw-r--r--src/mbgl/renderer/sources/render_custom_geometry_source.hpp50
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.cpp45
-rw-r--r--src/mbgl/renderer/sources/render_geojson_source.hpp16
-rw-r--r--src/mbgl/renderer/sources/render_image_source.cpp165
-rw-r--r--src/mbgl/renderer/sources/render_image_source.hpp29
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.cpp23
-rw-r--r--src/mbgl/renderer/sources/render_raster_source.hpp14
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.cpp25
-rw-r--r--src/mbgl/renderer/sources/render_vector_source.hpp14
-rw-r--r--src/mbgl/renderer/tile_mask.hpp15
-rw-r--r--src/mbgl/renderer/tile_parameters.hpp5
-rw-r--r--src/mbgl/renderer/tile_pyramid.cpp153
-rw-r--r--src/mbgl/renderer/tile_pyramid.hpp21
-rw-r--r--src/mbgl/renderer/update_parameters.hpp15
-rw-r--r--src/mbgl/shaders/circle.cpp60
-rw-r--r--src/mbgl/shaders/collision_box.cpp45
-rw-r--r--src/mbgl/shaders/collision_circle.cpp83
-rw-r--r--src/mbgl/shaders/collision_circle.hpp16
-rw-r--r--src/mbgl/shaders/debug.cpp2
-rw-r--r--src/mbgl/shaders/fill.cpp12
-rw-r--r--src/mbgl/shaders/fill_extrusion.cpp28
-rw-r--r--src/mbgl/shaders/fill_extrusion_pattern.cpp28
-rw-r--r--src/mbgl/shaders/fill_outline.cpp12
-rw-r--r--src/mbgl/shaders/fill_outline_pattern.cpp8
-rw-r--r--src/mbgl/shaders/fill_pattern.cpp8
-rw-r--r--src/mbgl/shaders/line.cpp43
-rw-r--r--src/mbgl/shaders/line_pattern.cpp38
-rw-r--r--src/mbgl/shaders/line_sdf.cpp48
-rw-r--r--src/mbgl/shaders/preludes.cpp27
-rw-r--r--src/mbgl/shaders/raster.cpp13
-rw-r--r--src/mbgl/shaders/shaders.cpp1
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp97
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp174
-rw-r--r--src/mbgl/sprite/sprite_loader.cpp4
-rw-r--r--src/mbgl/storage/resource.cpp22
-rw-r--r--src/mbgl/storage/response.cpp1
-rw-r--r--src/mbgl/style/conversion/constant.cpp94
-rw-r--r--src/mbgl/style/conversion/coordinate.cpp29
-rw-r--r--src/mbgl/style/conversion/filter.cpp248
-rw-r--r--src/mbgl/style/conversion/geojson.cpp18
-rw-r--r--src/mbgl/style/conversion/geojson_options.cpp85
-rw-r--r--src/mbgl/style/conversion/get_json_type.cpp34
-rw-r--r--src/mbgl/style/conversion/json.hpp2
-rw-r--r--src/mbgl/style/conversion/layer.cpp206
-rw-r--r--src/mbgl/style/conversion/light.cpp115
-rw-r--r--src/mbgl/style/conversion/make_property_setters.hpp209
-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)14
-rw-r--r--src/mbgl/style/conversion/source.cpp175
-rw-r--r--src/mbgl/style/conversion/tileset.cpp73
-rw-r--r--src/mbgl/style/conversion/transition_options.cpp40
-rw-r--r--src/mbgl/style/custom_tile_loader.cpp109
-rw-r--r--src/mbgl/style/custom_tile_loader.hpp45
-rw-r--r--src/mbgl/style/expression/array_assertion.cpp86
-rw-r--r--src/mbgl/style/expression/assertion.cpp73
-rw-r--r--src/mbgl/style/expression/at.cpp64
-rw-r--r--src/mbgl/style/expression/boolean_operator.cpp87
-rw-r--r--src/mbgl/style/expression/case.cpp91
-rw-r--r--src/mbgl/style/expression/check_subtype.cpp60
-rw-r--r--src/mbgl/style/expression/coalesce.cpp62
-rw-r--r--src/mbgl/style/expression/coercion.cpp144
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp568
-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.cpp211
-rw-r--r--src/mbgl/style/expression/is_constant.cpp40
-rw-r--r--src/mbgl/style/expression/is_expression.cpp29
-rw-r--r--src/mbgl/style/expression/let.cpp92
-rw-r--r--src/mbgl/style/expression/literal.cpp108
-rw-r--r--src/mbgl/style/expression/match.cpp263
-rw-r--r--src/mbgl/style/expression/parsing_context.cpp208
-rw-r--r--src/mbgl/style/expression/step.cpp152
-rw-r--r--src/mbgl/style/expression/util.cpp39
-rw-r--r--src/mbgl/style/expression/util.hpp14
-rw-r--r--src/mbgl/style/expression/value.cpp322
-rw-r--r--src/mbgl/style/function/categorical_stops.cpp3
-rw-r--r--src/mbgl/style/function/expression.cpp38
-rw-r--r--src/mbgl/style/function/identity_stops.cpp29
-rw-r--r--src/mbgl/style/image_impl.hpp1
-rw-r--r--src/mbgl/style/layers/circle_layer.cpp27
-rw-r--r--src/mbgl/style/layers/circle_layer_properties.hpp5
-rw-r--r--src/mbgl/style/layers/custom_layer.cpp11
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.cpp2
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.hpp2
-rw-r--r--src/mbgl/style/layers/line_layer.cpp6
-rw-r--r--src/mbgl/style/layers/line_layer_properties.hpp2
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp56
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp22
-rw-r--r--src/mbgl/style/observer.hpp3
-rw-r--r--src/mbgl/style/parser.cpp7
-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/geojson_source_impl.cpp9
-rw-r--r--src/mbgl/style/sources/image_source.cpp7
-rw-r--r--src/mbgl/style/sources/image_source_impl.cpp8
-rw-r--r--src/mbgl/style/sources/image_source_impl.hpp6
-rw-r--r--src/mbgl/style/style.cpp16
-rw-r--r--src/mbgl/style/style_impl.cpp48
-rw-r--r--src/mbgl/style/style_impl.hpp12
-rw-r--r--src/mbgl/style/types.cpp21
-rw-r--r--src/mbgl/text/collision_feature.cpp84
-rw-r--r--src/mbgl/text/collision_feature.hpp49
-rw-r--r--src/mbgl/text/collision_index.cpp359
-rw-r--r--src/mbgl/text/collision_index.hpp70
-rw-r--r--src/mbgl/text/collision_tile.cpp235
-rw-r--r--src/mbgl/text/collision_tile.hpp67
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.cpp165
-rw-r--r--src/mbgl/text/cross_tile_symbol_index.hpp64
-rw-r--r--src/mbgl/text/glyph.hpp25
-rw-r--r--src/mbgl/text/glyph_manager.cpp29
-rw-r--r--src/mbgl/text/glyph_manager.hpp11
-rw-r--r--src/mbgl/text/local_glyph_rasterizer.hpp46
-rw-r--r--src/mbgl/text/placement.cpp332
-rw-r--r--src/mbgl/text/placement.hpp91
-rw-r--r--src/mbgl/text/placement_config.hpp25
-rw-r--r--src/mbgl/text/quads.cpp320
-rw-r--r--src/mbgl/text/quads.hpp31
-rw-r--r--src/mbgl/text/shaping.cpp108
-rw-r--r--src/mbgl/text/shaping.hpp11
-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.cpp123
-rw-r--r--src/mbgl/tile/geojson_tile.hpp2
-rw-r--r--src/mbgl/tile/geojson_tile_data.hpp94
-rw-r--r--src/mbgl/tile/geometry_tile.cpp151
-rw-r--r--src/mbgl/tile/geometry_tile.hpp63
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp104
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp16
-rw-r--r--src/mbgl/tile/raster_tile.cpp36
-rw-r--r--src/mbgl/tile/raster_tile.hpp18
-rw-r--r--src/mbgl/tile/raster_tile_worker.cpp10
-rw-r--r--src/mbgl/tile/raster_tile_worker.hpp2
-rw-r--r--src/mbgl/tile/tile.cpp9
-rw-r--r--src/mbgl/tile/tile.hpp44
-rw-r--r--src/mbgl/tile/tile_id_hash.cpp29
-rw-r--r--src/mbgl/tile/tile_id_io.cpp5
-rw-r--r--src/mbgl/tile/tile_loader.hpp12
-rw-r--r--src/mbgl/tile/tile_loader_impl.hpp61
-rw-r--r--src/mbgl/tile/vector_tile.cpp8
-rw-r--r--src/mbgl/tile/vector_tile.hpp7
-rw-r--r--src/mbgl/util/chrono.cpp11
-rw-r--r--src/mbgl/util/constants.cpp2
-rw-r--r--src/mbgl/util/dtoa.cpp16
-rw-r--r--src/mbgl/util/dtoa.hpp1
-rw-r--r--src/mbgl/util/geojson_impl.cpp (renamed from src/mbgl/util/geojson.cpp)0
-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.cpp2
-rw-r--r--src/mbgl/util/http_timeout.cpp2
-rw-r--r--src/mbgl/util/i18n.cpp23
-rw-r--r--src/mbgl/util/i18n.hpp4
-rw-r--r--src/mbgl/util/io.cpp6
-rw-r--r--src/mbgl/util/mapbox.cpp6
-rw-r--r--src/mbgl/util/mapbox.hpp4
-rw-r--r--src/mbgl/util/mat4.cpp9
-rw-r--r--src/mbgl/util/math.hpp17
-rw-r--r--src/mbgl/util/offscreen_texture.cpp32
-rw-r--r--src/mbgl/util/offscreen_texture.hpp18
-rw-r--r--src/mbgl/util/std.hpp8
-rw-r--r--src/mbgl/util/thread_local.hpp44
-rw-r--r--src/mbgl/util/tile_cover.cpp25
-rw-r--r--src/mbgl/util/tile_cover.hpp5
-rw-r--r--src/mbgl/util/tiny_sdf.cpp105
-rw-r--r--src/mbgl/util/tiny_sdf.hpp20
-rw-r--r--src/parsedate/parsedate.c18
-rw-r--r--test/actor/actor.test.cpp77
-rw-r--r--test/actor/actor_ref.test.cpp80
-rw-r--r--test/algorithm/generate_clip_ids.test.cpp425
-rw-r--r--test/algorithm/mock.hpp2
-rw-r--r--test/algorithm/update_renderables.test.cpp992
-rw-r--r--test/algorithm/update_tile_masks.test.cpp132
-rw-r--r--test/api/annotations.test.cpp89
-rw-r--r--test/api/api_misuse.test.cpp16
-rw-r--r--test/api/custom_geometry_source.test.cpp71
-rw-r--r--test/api/custom_layer.test.cpp15
-rw-r--r--test/api/query.test.cpp47
-rw-r--r--test/api/recycle_map.cpp58
-rw-r--r--test/api/render_missing.test.cpp65
-rw-r--r--test/api/repeated_render.test.cpp76
-rw-r--r--test/api/zoom_history.cpp71
-rw-r--r--test/fixtures/annotations/debug_sparse/expected.pngbin2933 -> 1387 bytes
-rw-r--r--test/fixtures/api/empty-zoomed.json6
-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/nocontent/expected.pngbin0 -> 1685 bytes
-rw-r--r--test/fixtures/map/offline/expected.pngbin47843 -> 48705 bytes
-rw-r--r--test/fixtures/map/prefetch/empty.json5
-rw-r--r--test/fixtures/map/prefetch/expected.pngbin0 -> 2198 bytes
-rw-r--r--test/fixtures/map/prefetch/style.json24
-rw-r--r--test/fixtures/map/prefetch/tile_green.pngbin0 -> 659 bytes
-rw-r--r--test/fixtures/map/prefetch/tile_red.pngbin0 -> 659 bytes
-rw-r--r--test/fixtures/offline_database/v5.dbbin0 -> 19456 bytes
-rw-r--r--test/fixtures/offline_database/v999.dbbin0 -> 19456 bytes
-rw-r--r--test/fixtures/recycle_map/default_marker/expected.pngbin0 -> 1043 bytes
-rw-r--r--test/fixtures/recycle_map/flipped_marker/expected.pngbin0 -> 1031 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/geojson-missing-properties.info.json6
-rw-r--r--test/fixtures/style_parser/geojson-missing-properties.style.json9
-rw-r--r--test/fixtures/zoom_history/expected.pngbin0 -> 3894 bytes
-rw-r--r--test/gl/bucket.test.cpp250
-rw-r--r--test/gl/context.test.cpp114
-rw-r--r--test/gl/object.test.cpp7
-rw-r--r--test/map/map.test.cpp462
-rw-r--r--test/map/prefetch.test.cpp89
-rw-r--r--test/map/transform.test.cpp21
-rw-r--r--test/programs/binary_program.test.cpp16
-rw-r--r--test/programs/symbol_program.test.cpp4
-rw-r--r--test/renderer/backend_scope.test.cpp112
-rw-r--r--test/renderer/image_manager.test.cpp13
-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/fake_file_source.hpp4
-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_geometry_tile_feature.hpp11
-rw-r--r--test/src/mbgl/test/util.cpp23
-rw-r--r--test/src/mbgl/test/util.hpp9
-rw-r--r--test/storage/default_file_source.test.cpp210
-rw-r--r--test/storage/http_file_source.test.cpp9
-rw-r--r--test/storage/offline.test.cpp9
-rw-r--r--test/storage/offline_database.test.cpp93
-rw-r--r--test/storage/online_file_source.test.cpp10
-rw-r--r--test/storage/resource.test.cpp8
-rwxr-xr-xtest/storage/server.js6
-rw-r--r--test/storage/sqlite.test.cpp11
-rw-r--r--test/style/conversion/function.test.cpp36
-rw-r--r--test/style/conversion/geojson_options.test.cpp41
-rw-r--r--test/style/conversion/layer.test.cpp8
-rw-r--r--test/style/conversion/light.test.cpp7
-rw-r--r--test/style/expression/expression.test.cpp91
-rw-r--r--test/style/expression/util.test.cpp23
-rw-r--r--test/style/filter.test.cpp9
-rw-r--r--test/style/function/source_function.test.cpp9
-rw-r--r--test/style/source.test.cpp51
-rw-r--r--test/style/style.test.cpp18
-rw-r--r--test/text/cross_tile_symbol_index.test.cpp85
-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/text/quads.test.cpp44
-rw-r--r--test/tile/annotation_tile.test.cpp98
-rw-r--r--test/tile/custom_geometry_tile.test.cpp130
-rw-r--r--test/tile/geojson_tile.test.cpp45
-rw-r--r--test/tile/raster_tile.test.cpp27
-rw-r--r--test/tile/tile_id.test.cpp146
-rw-r--r--test/tile/vector_tile.test.cpp25
-rw-r--r--test/util/dtoa.test.cpp24
-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.cpp62
-rw-r--r--test/util/merge_lines.test.cpp112
-rw-r--r--test/util/offscreen_texture.test.cpp23
-rw-r--r--test/util/thread_local.test.cpp36
-rw-r--r--test/util/tile_cover.test.cpp9
-rw-r--r--test/util/unique_any.test.cpp186
1490 files changed, 72304 insertions, 40173 deletions
diff --git a/.gitignore b/.gitignore
index 653ff30c45..ce65e4c9b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,8 +19,8 @@ xcuserdata
/test/fixtures/offline_database/offline.db-*
/test/fixtures/offline_database/invalid.db
/test/fixtures/offline_database/invalid.db-*
-/test/fixtures/offline_database/v*.db
-/test/fixtures/offline_database/v*.db-*
+/test/fixtures/offline_database/migrated.db
+/test/fixtures/offline_database/migrated.db-*
/test/fixtures/**/actual.png
/test/fixtures/**/diff.png
/test/output
@@ -33,3 +33,4 @@ xcuserdata
/documentation
test/fixtures/api/assets.zip
test/fixtures/storage/assets.zip
+/.circle-week
diff --git a/.gitmodules b/.gitmodules
index 72cbe56da7..422fc3930e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "platform/ios/vendor/SMCalloutView"]
- path = platform/ios/vendor/SMCalloutView
- url = https://github.com/nfarina/calloutview.git
[submodule "platform/ios/uitest/KIF"]
path = platform/ios/uitest/KIF
url = https://github.com/kif-framework/KIF.git
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index c8d5746d68..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-language: cpp
-
-script:
- - true
-
-notifications:
- slack:
- secure: HHQYr7sF8M1SzoWSqgKVYtwAgGdLLCyTMsQjFhEEQNYO92ZwURE5s03qWTGH5k8+4Yqn26yrXt3NztLC4JIOpcGervN2mSZyq4dZgFTcWEd61igw0qwSenlwvFfbE1ASK/KYCzfyn9MIfHN+ovwLoRxXZkPwinKDvl3DXjBaFNg=
diff --git a/.tx/config b/.tx/config
index b464acb01a..1de8f78303 100644
--- a/.tx/config
+++ b/.tx/config
@@ -9,6 +9,13 @@ source_file = platform/darwin/resources/Base.lproj/Foundation.strings
source_lang = en
type = STRINGS
+[mapbox-gl-native.foundationstringsdict-darwin]
+file_filter = platform/darwin/resources/<lang>.lproj/Foundation.stringsdict
+lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant
+source_file = platform/darwin/resources/en.lproj/Foundation.stringsdict
+source_lang = en
+type = STRINGSDICT
+
[mapbox-gl-native.localizablestrings-ios]
file_filter = platform/ios/resources/<lang>.lproj/Localizable.strings
lang_map = pt_BR: pt-BR, pt_PT: pt-PT, zh_CN: zh-Hans, zh_TW: zh-Hant
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/CHANGELOG.md b/CHANGELOG.md
index 5034146cae..4ce6fe25be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,6 @@
Each Mapbox GL Native SDK has a separate changelog that highlights changes relevant to their respective platforms:
* [Mapbox Android SDK](platform/android/CHANGELOG.md)
-* [Mapbox iOS SDK](platform/ios/CHANGELOG.md)
-* [Mapbox macOS SDK](platform/macos/CHANGELOG.md)
+* [Mapbox Maps SDK for iOS](platform/ios/CHANGELOG.md)
+* [Mapbox Maps SDK for macOS](platform/macos/CHANGELOG.md)
* [node-mapbox-gl-native](platform/node/CHANGELOG.md)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e84b3a6da2..88368abbd5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,10 @@
-cmake_minimum_required(VERSION 3.1)
+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)
@@ -12,9 +13,13 @@ option(WITH_EGL "Use EGL backend" OFF)
if(WITH_CXX11ABI)
set(MASON_CXXABI_SUFFIX -cxx11abi)
- add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
+ endif()
else()
- add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
+ endif()
endif()
if(WITH_OSMESA AND WITH_EGL)
@@ -41,24 +46,23 @@ endif()
set_source_files_properties(src/mbgl/util/version.cpp PROPERTIES COMPILE_DEFINITIONS MBGL_VERSION_REV="${MBGL_VERSION_REV}")
-mason_use(geometry VERSION 0.9.0 HEADER_ONLY)
+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(geojsonvt VERSION 6.2.0 HEADER_ONLY)
-mason_use(supercluster VERSION 0.2.0-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)
-mason_use(earcut VERSION 0.12.3 HEADER_ONLY)
-mason_use(protozero VERSION 1.4.2 HEADER_ONLY)
+mason_use(earcut VERSION 0.12.4 HEADER_ONLY)
+mason_use(protozero VERSION 1.5.2 HEADER_ONLY)
mason_use(pixelmatch VERSION 0.10.0 HEADER_ONLY)
-mason_use(geojson VERSION 0.4.0 HEADER_ONLY)
-mason_use(polylabel VERSION 1.0.2 HEADER_ONLY)
-mason_use(wagyu VERSION 0.4.1 HEADER_ONLY)
-mason_use(shelf-pack VERSION 2.1.0 HEADER_ONLY)
-mason_use(cheap-ruler VERSION 2.5.1 HEADER_ONLY)
-mason_use(vector-tile VERSION 1.0.0-rc6 HEADER_ONLY)
+mason_use(geojson VERSION 0.4.2 HEADER_ONLY)
+mason_use(polylabel VERSION 1.0.3 HEADER_ONLY)
+mason_use(wagyu VERSION 0.4.3 HEADER_ONLY)
+mason_use(shelf-pack VERSION 2.1.1 HEADER_ONLY)
+mason_use(cheap-ruler VERSION 2.5.3 HEADER_ONLY)
+mason_use(vector-tile VERSION 1.0.0-rc7 HEADER_ONLY)
add_definitions(-DRAPIDJSON_HAS_STDSTRING=1)
@@ -67,28 +71,72 @@ if(WITH_COVERAGE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage")
endif(WITH_COVERAGE)
-set(CMAKE_CONFIGURATION_TYPES Debug Release)
+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")
-if(APPLE)
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -ftemplate-depth=1024 -fPIC -fvisibility=hidden -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Werror -Wno-variadic-macros -Wno-unknown-pragmas")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fvisibility=hidden -Wall -Wextra -Wshadow -Werror -Wno-variadic-macros -Wno-unknown-pragmas")
+if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
# -Wno-error=unused-command-line-argument is required due to https://llvm.org/bugs/show_bug.cgi?id=7798
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unused-command-line-argument")
endif()
set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG")
+set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -DNDEBUG")
+set(CMAKE_CXX_FLAGS_SANITIZE "-O1 -g -fno-omit-frame-pointer -fno-optimize-sibling-calls")
-if(CMAKE_COMPILER_IS_GNUCXX)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=unknown-warning-option")
+elseif(CMAKE_COMPILER_IS_GNUCXX)
# https://svn.boost.org/trac/boost/ticket/9240
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals")
endif()
+# Technique from https://crascit.com/2016/04/09/using-ccache-with-cmake/
+find_program(CCACHE_PROGRAM ccache)
+if(CCACHE_PROGRAM)
+ set(C_LAUNCHER "${CCACHE_PROGRAM}")
+ set(CXX_LAUNCHER "${CCACHE_PROGRAM}")
+
+ if(CMAKE_GENERATOR STREQUAL "Xcode")
+ # Set Xcode project attributes to route compilation and linking through our scripts
+ # Xcode doesn't include the path to the compiler/linker by default, so we'll have to add it.
+ configure_file(scripts/launch-c-xcode.in launch-c @ONLY)
+ configure_file(scripts/launch-cxx-xcode.in launch-cxx @ONLY)
+
+ set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_BINARY_DIR}/launch-c")
+ set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_BINARY_DIR}/launch-cxx")
+ set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_BINARY_DIR}/launch-c")
+ set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_BINARY_DIR}/launch-cxx")
+ else()
+ # Support Unix Makefiles and Ninja
+ configure_file(scripts/launch-c.in launch-c @ONLY)
+ configure_file(scripts/launch-cxx.in launch-cxx @ONLY)
+
+ set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_BINARY_DIR}/launch-c")
+ set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_BINARY_DIR}/launch-cxx")
+ endif()
+
+ execute_process(COMMAND chmod a+rx "${CMAKE_BINARY_DIR}/launch-c" "${CMAKE_BINARY_DIR}/launch-cxx")
+
+ if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+ # ccache splits up the compile steps, so we end up with unused arguments in some steps.
+ # Clang also thinks that ccache isn't interactive, so we explicitly need to enable color.
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qunused-arguments -fcolor-diagnostics")
+ endif()
+else()
+ message(STATUS "Can't find ccache — consider installing ccache to improve recompilation performance")
+endif()
+
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/platform/${MBGL_PLATFORM}/config.cmake)
message(ERROR "Can't find config.cmake file for platform ${MBGL_PLATFORM}")
endif()
-include(cmake/loop-uv.cmake)
-
include(platform/${MBGL_PLATFORM}/config.cmake)
+if (COMMAND mbgl_filesource)
+ include(cmake/filesource.cmake)
+endif()
+
include(cmake/core-files.cmake)
include(cmake/core.cmake)
@@ -121,5 +169,6 @@ endif()
if(CMAKE_GENERATOR STREQUAL "Xcode")
write_xcconfig_target_properties(
mbgl-core
+ mbgl-filesource
)
endif()
diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md
new file mode 100644
index 0000000000..7fcca0c822
--- /dev/null
+++ b/CODE-OF-CONDUCT.md
@@ -0,0 +1,5 @@
+# Code of conduct
+
+Everyone is invited to participate in Mapbox’s open source projects and public discussions: we want to create a welcoming and friendly environment. Harassment of participants or other unethical and unprofessional behavior will not be tolerated in our spaces. The [Contributor Covenant](http://contributor-covenant.org) applies to all projects under the Mapbox organization and we ask that you please read [the full text](http://contributor-covenant.org/version/1/2/0/).
+
+You can learn more about our open source philosophy on [mapbox.com](https://www.mapbox.com/about/open/).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a1c3e02070..f996a17730 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,8 +10,8 @@ If you want to contribute code:
1. Pull requests are gladly accepted. If there are any changes that developers using one of the GL SDKs should be aware of, please update the **master** section of the relevant changelog(s):
* [Mapbox Android SDK](platform/android/CHANGELOG.md)
- * [Mapbox iOS SDK](platform/ios/CHANGELOG.md)
- * [Mapbox macOS SDK](platform/macos/CHANGELOG.md)
+ * [Mapbox Maps SDK for iOS](platform/ios/CHANGELOG.md)
+ * [Mapbox Maps SDK for macOS](platform/macos/CHANGELOG.md)
* [node-mapbox-gl-native](platform/node/CHANGELOG.md)
1. Prefix your commit messages with the platform(s) your changes affect: `[core]`, `[android]`, `[ios]`, `[macos]`, `[node]`, or `[qt]`.
@@ -34,8 +34,3 @@ We’ve color-coded our labels by facet to make them easier to use:
* non-actionable status (grey)
* importance / urgency (green)
* topic / project / misc (yellow)
-
-# Code of conduct
-Everyone is invited to participate in Mapbox’s open source projects and public discussions: we want to create a welcoming and friendly environment. Harassment of participants or other unethical and unprofessional behavior will not be tolerated in our spaces. The [Contributor Covenant](http://contributor-covenant.org) applies to all projects under the Mapbox organization and we ask that you please read [the full text](http://contributor-covenant.org/version/1/2/0/).
-
-You can learn more about our open source philosophy on [mapbox.com](https://www.mapbox.com/about/open/).
diff --git a/DEVELOPING.md b/DEVELOPING.md
new file mode 100644
index 0000000000..f70d1f94ea
--- /dev/null
+++ b/DEVELOPING.md
@@ -0,0 +1,123 @@
+# Modern C++ support
+
+Mapbox GL Native supports the C++14 standard, and encourages contributions to
+the source code using modern C++ idioms like return type deductions, generic
+lambdas, `std::optional` and alike. However, we do not support all the features
+from the final draft of the C++14 standard - we had to sacrifice support for
+these features in order to support GCC from version 4.9 onwards.
+
+The following C++14 features are **not supported** in Mapbox GL Native:
+
+## [C++14 variable templates](https://isocpp.org/wiki/faq/cpp14-language#variable-templates)
+
+Constructs like the example below are not supported:
+
+```C++
+template<typename T> constexpr T pi = T(3.14);
+```
+
+### Workarounds:
+
+- If the variable is an alias, use the call the alias points to: [example](https://github.com/mapbox/mapbox-gl-native/commit/f1ac757bd28351fd57113a1e16f6c2e00ab193c1#diff-711ce10b54a522c948efc9030ffab4fcL269)
+```C++
+// auto foo = pi<double>;
+auto foo = double(3.14);
+```
+
+- Replace variable templates with either functions or structs: [example 1](https://github.com/mapbox/mapbox-gl-native/commit/f1ac757bd28351fd57113a1e16f6c2e00ab193c1#diff-ffbe6cdfd30513aaa4749b4d959a5da6L58), [example 2](https://github.com/mapbox/mapbox-gl-native/commit/f1ac757bd28351fd57113a1e16f6c2e00ab193c1#diff-04af54dc8685cdc382ebe24466dc1d00L98)
+
+## [C++14 aggregates with non-static data member initializers](http://en.cppreference.com/w/cpp/language/aggregate_initialization)
+
+Constructs like the example below are not supported:
+
+```C++
+struct Foo {
+ int x = { 0 };
+};
+
+// error: no matching function for call to 'Foo::Foo(<brace-enclosed initializer list>)'
+int main() {
+ Foo foo { 0 };
+ return 0;
+}
+```
+
+### Workarounds
+- Replace data member initializers with default parameter values in default constructors:
+
+```C++
+struct Foo {
+ Foo(int x_ = 0) : x(x_) {}
+ int x;
+};
+
+int main() {
+ Foo foo { 0 }; // works with default constructor
+ return 0;
+}
+```
+
+- Replace bracket initialization with regular round brackets or none:
+
+```C++
+struct Foo {
+ Foo(int x_ = 0) : x(x_) {}
+ int x;
+};
+
+int main() {
+ Foo foo(); // works
+ Foo bar; // also works
+ return 0;
+}
+```
+
+## [Extended `constexpr` support](https://isocpp.org/wiki/faq/cpp14-language#extended-constexpr)
+
+GCC 4.9 strictly forbids `constexpr` usage in the following scenarios:
+- No local variable declarations (not `static` or `thread_local`, and no uninitialized variables)
+- Cannot mutate objects whose lifetime began with the constant expression evaluation
+- Disable usage of if, switch, for, while, do-while (not goto) inside constexpr expressions
+- Enforces that constexpr member functions are implicitly const
+
+```C++
+// sorry, unimplemented: use of the value of the object being constructed
+// in a constant expression
+struct Foo {
+ int x, y;
+ constexpr Foo(int i) : x(i), y(x) {}
+};
+
+// error: body of constexpr function 'constexpr int test1(int)' not a
+// return-statement
+constexpr int test1(int i) {
+ int j = i;
+ return j;
+}
+
+// error: body of constexpr function 'constexpr bool test2(int)' not a
+// return-statement
+constexpr bool test2(int i) {
+ if (i > 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+```
+
+### Workarounds
+
+- Either remove `constexpr` specifier or replace it with `inline` in case of
+ functions
+
+
+## [Polymorphic lambdas](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68278)
+
+GCC 5.2.0 crashes with polymorphic lambdas and this version of the compiler
+is currently used in Qt Automotive. Luckily polymorphic lambdas are rarely
+used/needed but we had one incident fixed by #9665.
+
+### Workarounds
+
+- Copy & Paste™ the code.
diff --git a/INSTALL.md b/INSTALL.md
index 0789f5e0af..fb619770a1 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,8 +1,8 @@
# Building & Developing Mapbox GL Native from Source
**Just trying to use Mapbox GL Native? You don't need to read this stuff! We
-provide [easy-to-install, prebuilt versions of the Mapbox SDKs for iOS and Android
-that you can download instantly and get started with fast](https://www.mapbox.com/mobile/).**
+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.
@@ -11,47 +11,54 @@ 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
+## 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
+## 2: Installing dependencies
These dependencies are required for all operating systems and all platform
targets.
- - Modern C++ compiler that supports `-std=c++14`
+ - Modern C++ compiler that supports `-std=c++14`\*
- clang++ 3.5 or later _or_
- - g++-5 or later
+ - 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
+### Additional dependencies for Linux
- [`libcurl`](http://curl.haxx.se/libcurl/) (depends on OpenSSL)
-### Additional Dependencies for macOS
+### 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:
-* [Mapbox Android SDK](platform/android/)
-* [Mapbox iOS SDK](platform/ios/)
-* [Mapbox macOS SDK](platform/macos/)
+* [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/)
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 debca8730a..345f59c553 100644
--- a/Makefile
+++ b/Makefile
@@ -2,9 +2,11 @@ export BUILDTYPE ?= Debug
export WITH_CXX11ABI ?= $(shell scripts/check-cxx11abi.sh)
ifeq ($(BUILDTYPE), Release)
+else ifeq ($(BUILDTYPE), RelWithDebInfo)
+else ifeq ($(BUILDTYPE), Sanitize)
else ifeq ($(BUILDTYPE), Debug)
else
- $(error BUILDTYPE must be Debug or Release)
+ $(error BUILDTYPE must be Debug, Sanitize, Release or RelWithDebInfo)
endif
buildtype := $(shell echo "$(BUILDTYPE)" | tr "[A-Z]" "[a-z]")
@@ -70,31 +72,10 @@ MACOS_XCODEBUILD = xcodebuild \
-configuration $(BUILDTYPE) \
-workspace $(MACOS_WORK_PATH)
-
-MACOS_XCSCHEMES += platform/macos/scripts/executable.xcscheme
-MACOS_XCSCHEMES += platform/macos/scripts/library.xcscheme
-MACOS_XCSCHEMES += platform/macos/scripts/node.xcscheme
-
-$(MACOS_PROJ_PATH): $(BUILD_DEPS) $(MACOS_USER_DATA_PATH)/WorkspaceSettings.xcsettings $(MACOS_XCSCHEMES)
+$(MACOS_PROJ_PATH): $(BUILD_DEPS) $(MACOS_USER_DATA_PATH)/WorkspaceSettings.xcsettings
mkdir -p $(MACOS_OUTPUT_PATH)
(cd $(MACOS_OUTPUT_PATH) && cmake -G Xcode ../..)
- @# Create Xcode schemes so that we can use xcodebuild from the command line. CMake doesn't
- @# create these automatically.
- SCHEME_NAME=mbgl-test SCHEME_TYPE=executable platform/macos/scripts/create_scheme.sh
- SCHEME_NAME=mbgl-benchmark SCHEME_TYPE=executable platform/macos/scripts/create_scheme.sh
- SCHEME_NAME=mbgl-render SCHEME_TYPE=executable platform/macos/scripts/create_scheme.sh
- SCHEME_NAME=mbgl-offline SCHEME_TYPE=executable platform/macos/scripts/create_scheme.sh
- SCHEME_NAME=mbgl-glfw SCHEME_TYPE=executable platform/macos/scripts/create_scheme.sh
- SCHEME_NAME=mbgl-core SCHEME_TYPE=library BUILDABLE_NAME=libmbgl-core.a BLUEPRINT_NAME=mbgl-core platform/macos/scripts/create_scheme.sh
- SCHEME_NAME=mbgl-node SCHEME_TYPE=library BUILDABLE_NAME=mbgl-node.node BLUEPRINT_NAME=mbgl-node platform/macos/scripts/create_scheme.sh
-
- @# Create schemes for running node tests. These have all of the environment variables set to
- @# launch in the correct location.
- SCHEME_NAME="node tests" SCHEME_TYPE=node BUILDABLE_NAME=mbgl-node.node BLUEPRINT_NAME=mbgl-node NODE_ARGUMENT="`npm bin tape`/tape platform/node/test/js/**/*.test.js" platform/macos/scripts/create_scheme.sh
- SCHEME_NAME="node render tests" SCHEME_TYPE=node BUILDABLE_NAME=mbgl-node.node BLUEPRINT_NAME=mbgl-node NODE_ARGUMENT="platform/node/test/render.test.js" platform/macos/scripts/create_scheme.sh
- SCHEME_NAME="node query tests" SCHEME_TYPE=node BUILDABLE_NAME=mbgl-node.node BLUEPRINT_NAME=mbgl-node NODE_ARGUMENT="platform/node/test/query.test.js" platform/macos/scripts/create_scheme.sh
-
$(MACOS_USER_DATA_PATH)/WorkspaceSettings.xcsettings: platform/macos/WorkspaceSettings.xcsettings
mkdir -p "$(MACOS_USER_DATA_PATH)"
cp platform/macos/WorkspaceSettings.xcsettings "$@"
@@ -127,7 +108,15 @@ run-test-%: test
run-benchmark: run-benchmark-.
run-benchmark-%: benchmark
- $(MACOS_OUTPUT_PATH)/$(BUILDTYPE)/mbgl-benchmark --benchmark_filter=$*
+ $(MACOS_OUTPUT_PATH)/$(BUILDTYPE)/mbgl-benchmark --benchmark_filter=$* ${BENCHMARK_ARGS}
+
+.PHONY: node-benchmark
+node-benchmark: $(MACOS_PROJ_PATH)
+ set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'node-benchmark' build $(XCPRETTY)
+
+.PHONY: run-node-benchmark
+run-node-benchmark: node-benchmark
+ node platform/node/test/benchmark.js
.PHONY: glfw-app
glfw-app: $(MACOS_PROJ_PATH)
@@ -153,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
@@ -229,10 +222,23 @@ 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-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: ipackage
ipackage: $(IOS_PROJ_PATH)
FORMAT=$(FORMAT) BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=$(SYMBOLS) \
@@ -253,11 +259,6 @@ iframework: $(IOS_PROJ_PATH)
FORMAT=dynamic BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=$(SYMBOLS) \
./platform/ios/scripts/package.sh
-.PHONY: ifabric
-ifabric: $(IOS_PROJ_PATH)
- FORMAT=static BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=NO SELF_CONTAINED=YES \
- ./platform/ios/scripts/package.sh
-
.PHONY: ideploy
ideploy:
caffeinate -i ./platform/ios/scripts/deploy-packages.sh
@@ -278,7 +279,7 @@ darwin-update-examples:
.PHONY: check-public-symbols
check-public-symbols:
- node platform/darwin/scripts/check-public-symbols.js macOS
+ node platform/darwin/scripts/check-public-symbols.js macOS iOS
endif
#### Linux targets #####################################################
@@ -304,7 +305,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)
@@ -315,8 +316,12 @@ benchmark: $(LINUX_BUILD)
$(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-benchmark
ifneq (,$(shell command -v gdb 2> /dev/null))
- GDB = $(shell scripts/mason.sh PREFIX gdb VERSION 2017-04-08-aebcde5)/bin/gdb \
- -batch -return-child-result -ex 'set print thread-events off' -ex 'run' -ex 'thread apply all bt' --args
+ GDB ?= $(shell scripts/mason.sh PREFIX gdb VERSION 2017-04-08-aebcde5)/bin/gdb \
+ -batch -return-child-result \
+ -ex 'set print thread-events off' \
+ -ex 'set disable-randomization off' \
+ -ex 'run' \
+ -ex 'thread apply all bt' --args
endif
.PHONY: run-test
@@ -394,7 +399,7 @@ $(QT_BUILD): $(BUILD_DEPS)
-DWITH_QT_DECODERS=${WITH_QT_DECODERS} \
-DWITH_QT_I18N=${WITH_QT_I18N} \
-DWITH_QT_4=${WITH_QT_4} \
- -DWITH_CXX11ABI=$(shell scripts/check-cxx11abi.sh) \
+ -DWITH_CXX11ABI=${WITH_CXX11ABI} \
-DWITH_COVERAGE=${WITH_COVERAGE})
ifeq ($(HOST_PLATFORM), macos)
@@ -410,17 +415,9 @@ $(MACOS_QT_PROJ_PATH): $(BUILD_DEPS)
-DWITH_QT_DECODERS=${WITH_QT_DECODERS} \
-DWITH_QT_I18N=${WITH_QT_I18N} \
-DWITH_QT_4=${WITH_QT_4} \
- -DWITH_CXX11ABI=$(shell scripts/check-cxx11abi.sh) \
+ -DWITH_CXX11ABI=${WITH_CXX11ABI} \
-DWITH_COVERAGE=${WITH_COVERAGE})
- @# Create Xcode schemes so that we can use xcodebuild from the command line. CMake doesn't
- @# create these automatically.
- XCODEPROJ=$(MACOS_QT_PROJ_PATH) SCHEME_NAME=mbgl-qt SCHEME_TYPE=executable platform/macos/scripts/create_scheme.sh
- XCODEPROJ=$(MACOS_QT_PROJ_PATH) SCHEME_NAME=mbgl-test SCHEME_TYPE=executable platform/macos/scripts/create_scheme.sh
- XCODEPROJ=$(MACOS_QT_PROJ_PATH) SCHEME_NAME=mbgl-benchmark SCHEME_TYPE=executable platform/macos/scripts/create_scheme.sh
- XCODEPROJ=$(MACOS_QT_PROJ_PATH) SCHEME_NAME=mbgl-core SCHEME_TYPE=library BUILDABLE_NAME=libmbgl-core.a BLUEPRINT_NAME=mbgl-core platform/macos/scripts/create_scheme.sh
- XCODEPROJ=$(MACOS_QT_PROJ_PATH) SCHEME_NAME=qmapboxgl SCHEME_TYPE=library BUILDABLE_NAME=libqmapboxgl.dylib BLUEPRINT_NAME=qmapboxgl platform/macos/scripts/create_scheme.sh
-
.PHONY: qtproj
qtproj: $(MACOS_QT_PROJ_PATH)
open $(MACOS_QT_PROJ_PATH)
@@ -460,6 +457,12 @@ test-node: node
npm test
npm run test-suite
+.PHONY: test-node-recycle-map
+test-node-recycle-map: node
+ npm test
+ npm run test-render -- --recycle-map --shuffle
+ npm run test-query
+
#### Android targets ###########################################################
MBGL_ANDROID_ABIS = arm-v5;armeabi
@@ -480,21 +483,7 @@ MBGL_ANDROID_GRADLE = ./gradlew --parallel --max-workers=$(JOBS) -Pmapbox.buildt
# Some devices return \r\n, so we'll have to remove the carriage return before concatenating.
MBGL_ANDROID_ACTIVE_ARCHS = $(shell adb devices | sed '1d;/^\*/d;s/[[:space:]].*//' | xargs -n 1 -I DEV `type -P adb` -s DEV shell getprop ro.product.cpu.abi | tr -d '\r')
-.PHONY: android-help
-android-help:
- @echo
- @echo "Available Android architecture targets:"
- @echo
- @echo " make android-arm-v5"
- @echo " make android-arm-v7, make android"
- @echo " make android-arm-v8"
- @echo " make android-mips"
- @echo " make android-mips-64"
- @echo " make android-x86"
- @echo " make android-x86-64"
- @echo " make apackage"
- @echo
-
+# Generate code based on the style specification
.PHONY: android-style-code
android-style-code:
node platform/android/scripts/generate-style-code.js
@@ -512,14 +501,17 @@ define ANDROID_RULES
android-test-lib-$1: platform/android/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
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
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:assemble$(BUILDTYPE)
+# Build the core test for specified abi
.PHONY: android-core-test-$1
android-core-test-$1: android-test-lib-$1
# Compile main sources and extract the classes (using the test app to get all transitive dependencies in one place)
@@ -552,28 +544,37 @@ run-android-core-test-$1-%: android-core-test-$1
rm -rf $(MBGL_ANDROID_CORE_TEST_DIR)/results && mkdir -p $(MBGL_ANDROID_CORE_TEST_DIR)/results
tar -xzf $(MBGL_ANDROID_CORE_TEST_DIR)/results.tgz --strip-components=2 -C $(MBGL_ANDROID_CORE_TEST_DIR)/results
+# Run the core test for specified abi
.PHONY: run-android-core-test-$1
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
- adb uninstall com.mapbox.mapboxsdk.testapp > /dev/null
+ -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
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:assembleDebug :MapboxGLAndroidSDKTestApp:assembleAndroidTest
-# This test assumes that you have Android Simulator started locally.
+# 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
- adb uninstall com.mapbox.mapboxsdk.testapp > /dev/null
+ -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
- adb uninstall com.mapbox.mapboxsdk.testapp > /dev/null
+ -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
+ adb logcat | ndk-stack -sym platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/debug/obj/$2/
+
endef
# Explodes the arguments into individual variables
@@ -583,39 +584,39 @@ endef
$(foreach abi,$(MBGL_ANDROID_ABIS),$(eval $(call ANDROID_RULES_INVOKER,$(subst ;, ,$(abi)))))
+# Build the Android SDK and test app with abi set to arm-v7
.PHONY: android
android: android-arm-v7
+# Build the Android SDK with abi set to arm-v7
.PHONY: android-lib
android-lib: android-lib-arm-v7
+# Run the test app on connected android device with abi set to arm-v7
.PHONY: run-android
run-android: run-android-arm-v7
+# Run Java Instrumentation tests on a connected android device or emulator with abi set to arm-v7
.PHONY: run-android-ui-test
run-android-ui-test: run-android-ui-test-arm-v7
run-android-ui-test-%: run-android-ui-test-arm-v7-%
-# Java-only test
+# 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
- cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDKTestApp:testDebugUnitTest
+ cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest
run-android-unit-test-%: platform/android/configuration.gradle
- cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDKTestApp:testDebugUnitTest --tests "$*"
-
-# Java-only test
-.PHONY: run-android-wear-unit-test
-run-android-wear-unit-test: platform/android/configuration.gradle
- cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDKWearTestApp:testDebugUnitTest
-run-android-wear-unit-test-%: platform/android/configuration.gradle
- cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDKWearTestApp:testDebugUnitTest --tests "$*"
+ cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest --tests "$*"
+# Run Instrumentation tests on AWS device farm, requires additional authentication through gradle.properties
.PHONY: run-android-ui-test-aws
run-android-ui-test-aws: platform/android/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all devicefarmUpload
+# Builds a release package of the Android SDK
.PHONY: apackage
apackage: platform/android/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)
# Uploads the compiled Android SDK to Maven
@@ -623,29 +624,57 @@ apackage: platform/android/configuration.gradle
run-android-upload-archives: platform/android/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDK:uploadArchives
+# Dump system graphics information for the test app
+.PHONY: android-gfxinfo
+android-gfxinfo:
+ adb shell dumpsys gfxinfo com.mapbox.mapboxsdk.testapp reset
+
# Runs Android UI tests on all connected devices using Spoon
.PHONY: run-android-ui-test-spoon
run-android-ui-test-spoon: platform/android/configuration.gradle
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis="$(MBGL_ANDROID_ACTIVE_ARCHS)" spoon
+# Generates Activity sanity tests
.PHONY: test-code-android
test-code-android:
node platform/android/scripts/generate-test-code.js
+# Runs checkstyle and lint on the Android code
+.PHONY: android-check
+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
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
+ 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
+ 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
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:javadocrelease
+# Symbolicate ndk stack traces for the arm-v7 abi
+.PHONY: android-ndk-stack
+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
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
@@ -668,7 +697,6 @@ clean:
./platform/android/MapboxGLAndroidSDK/build \
./platform/android/MapboxGLAndroidSDK/.externalNativeBuild \
./platform/android/MapboxGLAndroidSDKTestApp/build \
- ./platform/android/MapboxGLAndroidSDKWearTestApp/build \
./platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/gen \
./platform/android/MapboxGLAndroidSDK/src/main/assets
diff --git a/README.md b/README.md
index 94a534a12f..c66d2216a4 100644
--- a/README.md
+++ b/README.md
@@ -8,20 +8,23 @@ This repository hosts the cross-platform Mapbox GL Native library, plus convenie
| SDK | Languages | Build status |
| --------------------------------------- | ---------------------------------- | ---------------------------------------- |
-| [Mapbox GL Native](INSTALL.md) | C++14 | [![Travis](https://travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds) [![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 Android SDK](platform/android/) | Java | [![Bitrise](https://www.bitrise.io/app/79cdcbdc42de4303.svg?token=_InPF8bII6W7J6kFr-L8QQ&branch=master)](https://www.bitrise.io/app/79cdcbdc42de4303) |
-| [Mapbox iOS SDK](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 macOS SDK](platform/macos/) | Objective-C, Swift, or AppleScript | [![Bitrise](https://www.bitrise.io/app/155ef7da24b38dcd.svg?token=4KSOw_gd6WxTnvGE2rMttg&branch=master)](https://www.bitrise.io/app/155ef7da24b38dcd) |
-| [node-mapbox-gl-native](platform/node/) | Node.js | [![Linux](https://travis-ci.org/mapbox/mapbox-gl-native.svg?branch=master)](https://travis-ci.org/mapbox/mapbox-gl-native/builds) [![macOS](https://www.bitrise.io/app/55e3a9bf71202106.svg?token=5qf5ZUcKVN3LDnHhW7rO0w)](https://www.bitrise.io/app/55e3a9bf71202106) |
-| [Mapbox Qt SDK](platform/qt) | C++03 | [![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) |
+| [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) |
+| [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) |
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: | |
+| [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 |
| [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 |
| [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
+[![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/benchmark/api/query.benchmark.cpp b/benchmark/api/query.benchmark.cpp
index 05732e0e9a..7694585526 100644
--- a/benchmark/api/query.benchmark.cpp
+++ b/benchmark/api/query.benchmark.cpp
@@ -1,11 +1,9 @@
#include <benchmark/benchmark.h>
-#include <mbgl/benchmark/util.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/renderer/renderer.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -24,21 +22,19 @@ public:
NetworkStatus::Set(NetworkStatus::Status::Offline);
fileSource.setAccessToken("foobar");
- map.getStyle().loadJSON(util::read_file("benchmark/fixtures/api/query_style.json"));
+ map.getStyle().loadJSON(util::read_file("benchmark/fixtures/api/style.json"));
map.setLatLngZoom({ 40.726989, -73.992857 }, 15); // Manhattan
map.getStyle().addImage(std::make_unique<style::Image>("test-icon",
decodeImage(util::read_file("benchmark/fixtures/api/default_marker.png")), 1.0));
- mbgl::benchmark::render(map, view);
+ frontend.render(map);
}
util::RunLoop loop;
- HeadlessBackend backend;
- BackendScope scope { backend };
- OffscreenView view{ backend.getContext(), { 1000, 1000 } };
DefaultFileSource fileSource{ "benchmark/fixtures/api/cache.db", "." };
ThreadPool threadPool{ 4 };
- Map map{ backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still };
+ HeadlessFrontend frontend { { 1000, 1000 }, 1, fileSource, threadPool };
+ Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Static};
ScreenBox box{{ 0, 0 }, { 1000, 1000 }};
};
@@ -48,7 +44,7 @@ static void API_queryRenderedFeaturesAll(::benchmark::State& state) {
QueryBenchmark bench;
while (state.KeepRunning()) {
- bench.map.queryRenderedFeatures(bench.box);
+ bench.frontend.getRenderer()->queryRenderedFeatures(bench.box, {});
}
}
@@ -56,7 +52,7 @@ static void API_queryRenderedFeaturesLayerFromLowDensity(::benchmark::State& sta
QueryBenchmark bench;
while (state.KeepRunning()) {
- bench.map.queryRenderedFeatures(bench.box, {{{ "testlayer" }}, {}});
+ bench.frontend.getRenderer()->queryRenderedFeatures(bench.box, {{{ "testlayer" }}, {}});
}
}
@@ -64,7 +60,7 @@ static void API_queryRenderedFeaturesLayerFromHighDensity(::benchmark::State& st
QueryBenchmark bench;
while (state.KeepRunning()) {
- bench.map.queryRenderedFeatures(bench.box, {{{"road-street" }}, {}});
+ bench.frontend.getRenderer()->queryRenderedFeatures(bench.box, {{{"road-street" }}, {}});
}
}
diff --git a/benchmark/api/render.benchmark.cpp b/benchmark/api/render.benchmark.cpp
new file mode 100644
index 0000000000..a1b557777f
--- /dev/null
+++ b/benchmark/api/render.benchmark.cpp
@@ -0,0 +1,78 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/map/map_observer.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/network_status.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+using namespace mbgl;
+
+namespace {
+
+class RenderBenchmark {
+public:
+ RenderBenchmark() {
+ NetworkStatus::Set(NetworkStatus::Status::Offline);
+ fileSource.setAccessToken("foobar");
+ }
+
+ util::RunLoop loop;
+ DefaultFileSource fileSource { "benchmark/fixtures/api/cache.db", "." };
+ ThreadPool threadPool { 4 };
+};
+
+static void prepare(Map& map, optional<std::string> json = {}) {
+ map.getStyle().loadJSON(json ? *json : util::read_file("benchmark/fixtures/api/style.json"));
+ map.setLatLngZoom({ 40.726989, -73.992857 }, 15); // Manhattan
+ map.getStyle().addImage(std::make_unique<style::Image>("test-icon",
+ decodeImage(util::read_file("benchmark/fixtures/api/default_marker.png")), 1.0));
+}
+
+} // end namespace
+
+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::Static};
+ prepare(map);
+
+ while (state.KeepRunning()) {
+ frontend.render(map);
+ }
+}
+
+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::Static};
+
+ while (state.KeepRunning()) {
+ prepare(map, { "{}" });
+ frontend.render(map);
+ prepare(map);
+ frontend.render(map);
+ }
+}
+
+static void API_renderStill_recreate_map(::benchmark::State& state) {
+ RenderBenchmark bench;
+
+ 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::Static};
+ prepare(map);
+ frontend.render(map);
+ }
+}
+
+BENCHMARK(API_renderStill_reuse_map);
+BENCHMARK(API_renderStill_reuse_map_switch_styles);
+BENCHMARK(API_renderStill_recreate_map);
diff --git a/benchmark/fixtures/api/cache.db b/benchmark/fixtures/api/cache.db
index a62a3b0f00..6a1d60421f 100644
--- a/benchmark/fixtures/api/cache.db
+++ b/benchmark/fixtures/api/cache.db
Binary files differ
diff --git a/benchmark/fixtures/api/query_style.json b/benchmark/fixtures/api/query_style.json
deleted file mode 100644
index 54899c3951..0000000000
--- a/benchmark/fixtures/api/query_style.json
+++ /dev/null
@@ -1,11313 +0,0 @@
-{
- "created": 0,
- "draft": false,
- "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
- "id": "streets-v9",
- "layers": [
- {
- "id": "background",
- "layout": {},
- "paint": {
- "background-color": {
- "base": 1,
- "stops": [
- [
- 11,
- "hsl(35, 32%, 91%)"
- ],
- [
- 13,
- "hsl(35, 12%, 89%)"
- ]
- ]
- }
- },
- "type": "background"
- },
- {
- "filter": [
- "==",
- "class",
- "snow"
- ],
- "id": "landcover_snow",
- "layout": {},
- "metadata": {
- "mapbox:group": "1456970288113.8113"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(0, 0%, 100%)",
- "fill-opacity": 0.2
- },
- "source": "composite",
- "source-layer": "landcover",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "wood"
- ],
- "id": "landcover_wood",
- "layout": {},
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1456970288113.8113"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(75, 62%, 81%)",
- "fill-opacity": {
- "base": 1.5,
- "stops": [
- [
- 2,
- 0.3
- ],
- [
- 7,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landcover",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "scrub"
- ],
- "id": "landcover_scrub",
- "layout": {},
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1456970288113.8113"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(75, 62%, 81%)",
- "fill-opacity": {
- "base": 1.5,
- "stops": [
- [
- 2,
- 0.3
- ],
- [
- 7,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landcover",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "grass"
- ],
- "id": "landcover_grass",
- "layout": {},
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1456970288113.8113"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(75, 62%, 81%)",
- "fill-opacity": {
- "base": 1.5,
- "stops": [
- [
- 2,
- 0.3
- ],
- [
- 7,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landcover",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "crop"
- ],
- "id": "landcover_crop",
- "layout": {},
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1456970288113.8113"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(75, 62%, 81%)",
- "fill-opacity": {
- "base": 1.5,
- "stops": [
- [
- 2,
- 0.3
- ],
- [
- 7,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landcover",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "national_park"
- ],
- "id": "national_park",
- "layout": {},
- "paint": {
- "fill-color": "hsl(100, 58%, 76%)",
- "fill-opacity": {
- "base": 1,
- "stops": [
- [
- 5,
- 0
- ],
- [
- 6,
- 0.5
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landuse_overlay",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "hospital"
- ],
- "id": "hospital",
- "layout": {},
- "paint": {
- "fill-color": {
- "base": 1,
- "stops": [
- [
- 15.5,
- "hsl(340, 37%, 87%)"
- ],
- [
- 16,
- "hsl(340, 63%, 89%)"
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landuse",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "school"
- ],
- "id": "school",
- "layout": {},
- "paint": {
- "fill-color": {
- "base": 1,
- "stops": [
- [
- 15.5,
- "hsl(50, 47%, 81%)"
- ],
- [
- 16,
- "hsl(50, 63%, 84%)"
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landuse",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "park"
- ],
- "id": "park",
- "layout": {},
- "paint": {
- "fill-color": "hsl(100, 58%, 76%)",
- "fill-opacity": {
- "base": 1,
- "stops": [
- [
- 5,
- 0
- ],
- [
- 6,
- 1
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landuse",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "pitch"
- ],
- "id": "pitch",
- "layout": {},
- "paint": {
- "fill-color": "hsl(100, 57%, 72%)"
- },
- "source": "composite",
- "source-layer": "landuse",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "pitch"
- ],
- "id": "pitch-line",
- "layout": {
- "line-join": "miter"
- },
- "minzoom": 15,
- "paint": {
- "line-color": "hsl(75, 57%, 84%)"
- },
- "source": "composite",
- "source-layer": "landuse",
- "type": "line"
- },
- {
- "filter": [
- "==",
- "class",
- "cemetery"
- ],
- "id": "cemetery",
- "layout": {},
- "paint": {
- "fill-color": "hsl(75, 37%, 81%)"
- },
- "source": "composite",
- "source-layer": "landuse",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "industrial"
- ],
- "id": "industrial",
- "layout": {},
- "paint": {
- "fill-color": {
- "base": 1,
- "stops": [
- [
- 15.5,
- "hsl(230, 15%, 86%)"
- ],
- [
- 16,
- "hsl(230, 29%, 89%)"
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "landuse",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "class",
- "sand"
- ],
- "id": "sand",
- "layout": {},
- "paint": {
- "fill-color": "hsl(60, 46%, 87%)"
- },
- "source": "composite",
- "source-layer": "landuse",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "level",
- 94
- ],
- "id": "hillshade_highlight_bright",
- "layout": {},
- "maxzoom": 16,
- "metadata": {
- "mapbox:group": "1456969573402.7817"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(0, 0%, 100%)",
- "fill-opacity": {
- "stops": [
- [
- 14,
- 0.12
- ],
- [
- 16,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "hillshade",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "level",
- 90
- ],
- "id": "hillshade_highlight_med",
- "layout": {},
- "maxzoom": 16,
- "metadata": {
- "mapbox:group": "1456969573402.7817"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(0, 0%, 100%)",
- "fill-opacity": {
- "stops": [
- [
- 14,
- 0.12
- ],
- [
- 16,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "hillshade",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "level",
- 89
- ],
- "id": "hillshade_shadow_faint",
- "layout": {},
- "maxzoom": 16,
- "metadata": {
- "mapbox:group": "1456969573402.7817"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(56, 59%, 22%)",
- "fill-opacity": {
- "stops": [
- [
- 14,
- 0.05
- ],
- [
- 16,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "hillshade",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "level",
- 78
- ],
- "id": "hillshade_shadow_med",
- "layout": {},
- "maxzoom": 16,
- "metadata": {
- "mapbox:group": "1456969573402.7817"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(56, 59%, 22%)",
- "fill-opacity": {
- "stops": [
- [
- 14,
- 0.05
- ],
- [
- 16,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "hillshade",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "level",
- 67
- ],
- "id": "hillshade_shadow_dark",
- "layout": {},
- "maxzoom": 16,
- "metadata": {
- "mapbox:group": "1456969573402.7817"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(56, 59%, 22%)",
- "fill-opacity": {
- "stops": [
- [
- 14,
- 0.06
- ],
- [
- 16,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "hillshade",
- "type": "fill"
- },
- {
- "filter": [
- "==",
- "level",
- 56
- ],
- "id": "hillshade_shadow_extreme",
- "layout": {},
- "maxzoom": 16,
- "metadata": {
- "mapbox:group": "1456969573402.7817"
- },
- "paint": {
- "fill-antialias": false,
- "fill-color": "hsl(56, 59%, 22%)",
- "fill-opacity": {
- "stops": [
- [
- 14,
- 0.06
- ],
- [
- 16,
- 0
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "hillshade",
- "type": "fill"
- },
- {
- "filter": [
- "in",
- "class",
- "canal",
- "river"
- ],
- "id": "waterway-river-canal",
- "layout": {
- "line-cap": {
- "base": 1,
- "stops": [
- [
- 0,
- "butt"
- ],
- [
- 11,
- "round"
- ]
- ]
- },
- "line-join": "round"
- },
- "minzoom": 8,
- "paint": {
- "line-color": "hsl(205, 87%, 76%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 8,
- 0
- ],
- [
- 8.5,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.3,
- "stops": [
- [
- 8.5,
- 0.1
- ],
- [
- 20,
- 8
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "waterway",
- "type": "line"
- },
- {
- "filter": [
- "!in",
- "class",
- "canal",
- "river"
- ],
- "id": "waterway-small",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(205, 87%, 76%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13,
- 0
- ],
- [
- 13.5,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.35,
- "stops": [
- [
- 13.5,
- 0.1
- ],
- [
- 20,
- 3
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "waterway",
- "type": "line"
- },
- {
- "id": "water-shadow",
- "layout": {},
- "paint": {
- "fill-color": "hsl(215, 84%, 69%)",
- "fill-opacity": 1,
- "fill-translate": {
- "base": 1.2,
- "stops": [
- [
- 7,
- [
- 0,
- 0
- ]
- ],
- [
- 16,
- [
- -1,
- -1
- ]
- ]
- ]
- },
- "fill-translate-anchor": "viewport"
- },
- "source": "composite",
- "source-layer": "water",
- "type": "fill"
- },
- {
- "id": "water",
- "paint": {
- "fill-color": "hsl(196, 80%, 70%)"
- },
- "ref": "water-shadow"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "Polygon"
- ],
- [
- "==",
- "class",
- "land"
- ]
- ],
- "id": "barrier_line-land-polygon",
- "layout": {},
- "paint": {
- "fill-color": "hsl(35, 12%, 89%)"
- },
- "source": "composite",
- "source-layer": "barrier_line",
- "type": "fill"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "==",
- "class",
- "land"
- ]
- ],
- "id": "barrier_line-land-line",
- "layout": {
- "line-cap": "round"
- },
- "paint": {
- "line-color": "hsl(35, 12%, 89%)",
- "line-width": {
- "base": 1.99,
- "stops": [
- [
- 14,
- 0.75
- ],
- [
- 20,
- 40
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "barrier_line",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "Polygon"
- ],
- [
- "!=",
- "type",
- "apron"
- ]
- ],
- "id": "aeroway-polygon",
- "layout": {},
- "metadata": {
- "mapbox:group": "1444934828655.3389"
- },
- "minzoom": 11,
- "paint": {
- "fill-color": {
- "base": 1,
- "stops": [
- [
- 15,
- "hsl(230, 23%, 82%)"
- ],
- [
- 16,
- "hsl(230, 37%, 84%)"
- ]
- ]
- },
- "fill-opacity": {
- "base": 1,
- "stops": [
- [
- 11,
- 0
- ],
- [
- 11.5,
- 1
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "aeroway",
- "type": "fill"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "==",
- "type",
- "runway"
- ]
- ],
- "id": "aeroway-runway",
- "layout": {},
- "metadata": {
- "mapbox:group": "1444934828655.3389"
- },
- "minzoom": 9,
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 15,
- "hsl(230, 23%, 82%)"
- ],
- [
- 16,
- "hsl(230, 37%, 84%)"
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 9,
- 1
- ],
- [
- 18,
- 80
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "aeroway",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "==",
- "type",
- "taxiway"
- ]
- ],
- "id": "aeroway-taxiway",
- "layout": {},
- "metadata": {
- "mapbox:group": "1444934828655.3389"
- },
- "minzoom": 9,
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 15,
- "hsl(230, 23%, 82%)"
- ],
- [
- 16,
- "hsl(230, 37%, 84%)"
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 0.5
- ],
- [
- 18,
- 20
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "aeroway",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "!=",
- "type",
- "building:part"
- ],
- [
- "==",
- "underground",
- "false"
- ]
- ],
- "id": "building-line",
- "layout": {},
- "minzoom": 15,
- "paint": {
- "line-color": "hsl(35, 6%, 79%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 15.5,
- 0
- ],
- [
- 16,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 0.75
- ],
- [
- 20,
- 3
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "building",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "!=",
- "type",
- "building:part"
- ],
- [
- "==",
- "underground",
- "false"
- ]
- ],
- "id": "building",
- "layout": {},
- "minzoom": 15,
- "paint": {
- "fill-color": {
- "base": 1,
- "stops": [
- [
- 15,
- "hsl(35, 11%, 88%)"
- ],
- [
- 16,
- "hsl(35, 8%, 85%)"
- ]
- ]
- },
- "fill-opacity": {
- "base": 1,
- "stops": [
- [
- 15.5,
- 0
- ],
- [
- 16,
- 1
- ]
- ]
- },
- "fill-outline-color": "hsl(35, 6%, 79%)"
- },
- "source": "composite",
- "source-layer": "building",
- "type": "fill"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "street"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-street-low",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "stops": [
- [
- 11.5,
- 0
- ],
- [
- 12,
- 1
- ],
- [
- 14,
- 1
- ],
- [
- 14.01,
- 0
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "street_limited"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-street_limited-low",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "stops": [
- [
- 11.5,
- 0
- ],
- [
- 12,
- 1
- ],
- [
- 14,
- 1
- ],
- [
- 14.01,
- 0
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "service",
- "track"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ],
- [
- "!=",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "tunnel-service-link-track-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 14,
- "paint": {
- "line-color": "hsl(230, 19%, 75%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "tunnel-street_limited-case",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(230, 19%, 75%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 13,
- 0
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "ref": "tunnel-street_limited-low"
- },
- {
- "id": "tunnel-street-case",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(230, 19%, 75%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 13,
- 0
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "ref": "tunnel-street-low"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "secondary",
- "tertiary"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-secondary-tertiary-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(230, 19%, 75%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 8.5,
- 0.5
- ],
- [
- 10,
- 0.75
- ],
- [
- 18,
- 26
- ]
- ]
- },
- "line-width": {
- "base": 1.2,
- "stops": [
- [
- 10,
- 0.75
- ],
- [
- 18,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "primary"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-primary-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(230, 19%, 75%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "structure",
- "tunnel"
- ],
- [
- "==",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "tunnel-trunk_link-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway_link"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-motorway_link-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "structure",
- "tunnel"
- ],
- [
- "==",
- "type",
- "trunk"
- ]
- ]
- ],
- "id": "tunnel-trunk-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-motorway-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": [
- 3,
- 3
- ],
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "construction"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-construction",
- "layout": {
- "line-join": "miter"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 14,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 0.4,
- 0.8
- ]
- ],
- [
- 15,
- [
- 0.3,
- 0.6
- ]
- ],
- [
- 16,
- [
- 0.2,
- 0.3
- ]
- ],
- [
- 17,
- [
- 0.2,
- 0.25
- ]
- ],
- [
- 18,
- [
- 0.15,
- 0.15
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "path"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ],
- [
- "!=",
- "type",
- "steps"
- ]
- ]
- ],
- "id": "tunnel-path",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(35, 26%, 95%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.75,
- 1
- ]
- ],
- [
- 16,
- [
- 1,
- 0.75
- ]
- ],
- [
- 17,
- [
- 1,
- 0.5
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 1
- ],
- [
- 18,
- 4
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "structure",
- "tunnel"
- ],
- [
- "==",
- "type",
- "steps"
- ]
- ]
- ],
- "id": "tunnel-steps",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(35, 26%, 95%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.75,
- 1
- ]
- ],
- [
- 16,
- [
- 1,
- 0.75
- ]
- ],
- [
- 17,
- [
- 0.3,
- 0.3
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 1
- ],
- [
- 16,
- 1.6
- ],
- [
- 18,
- 6
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "tunnel-trunk_link",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(46, 77%, 78%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "tunnel-trunk_link-case"
- },
- {
- "id": "tunnel-motorway_link",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(26, 100%, 78%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "tunnel-motorway_link-case"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "pedestrian"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-pedestrian",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.5,
- 0.4
- ]
- ],
- [
- 16,
- [
- 1,
- 0.2
- ]
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "tunnel-service-link-track",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- }
- },
- "ref": "tunnel-service-link-track-case"
- },
- {
- "id": "tunnel-street_limited",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(35, 14%, 93%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "tunnel-street_limited-low"
- },
- {
- "id": "tunnel-street",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "tunnel-street-low"
- },
- {
- "id": "tunnel-secondary-tertiary",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-blur": 0,
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 8.5,
- 0.5
- ],
- [
- 10,
- 0.75
- ],
- [
- 18,
- 26
- ]
- ]
- }
- },
- "ref": "tunnel-secondary-tertiary-case"
- },
- {
- "id": "tunnel-primary",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-blur": 0,
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "ref": "tunnel-primary-case"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "path",
- "pedestrian",
- "service",
- "track"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ],
- [
- "!=",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "tunnel-oneway-arrows-blue-minor",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 17,
- "oneway-small"
- ],
- [
- 18,
- "oneway-large"
- ]
- ]
- },
- "icon-padding": 2,
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 16,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "primary",
- "secondary",
- "street",
- "street_limited",
- "tertiary"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ],
- [
- "!=",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "tunnel-oneway-arrows-blue-major",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 16,
- "oneway-small"
- ],
- [
- 17,
- "oneway-large"
- ]
- ]
- },
- "icon-padding": 2,
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 15,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "trunk"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ]
- ]
- ],
- "id": "tunnel-trunk",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-color": "hsl(46, 77%, 78%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "tunnel-motorway",
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "paint": {
- "line-blur": 0,
- "line-color": "hsl(26, 100%, 78%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "ref": "tunnel-motorway-case"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "motorway",
- "motorway_link",
- "trunk"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "==",
- "structure",
- "tunnel"
- ],
- [
- "!in",
- "type",
- "primary_link",
- "secondary_link",
- "tertiary_link"
- ]
- ]
- ],
- "id": "tunnel-oneway-arrows-white",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 16,
- "oneway-white-small"
- ],
- [
- 17,
- "oneway-white-large"
- ]
- ]
- },
- "icon-padding": 2,
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855769305.6016"
- },
- "minzoom": 16,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "==",
- "type",
- "ferry"
- ]
- ],
- "id": "ferry",
- "layout": {
- "line-join": "round"
- },
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 15,
- "hsl(205, 73%, 63%)"
- ],
- [
- 17,
- "hsl(230, 73%, 63%)"
- ]
- ]
- },
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 12,
- [
- 1,
- 0
- ]
- ],
- [
- 13,
- [
- 12,
- 4
- ]
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 20,
- 1
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "==",
- "type",
- "ferry_auto"
- ]
- ],
- "id": "ferry_auto",
- "layout": {
- "line-join": "round"
- },
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 15,
- "hsl(205, 73%, 63%)"
- ],
- [
- 17,
- "hsl(230, 73%, 63%)"
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 20,
- 1
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "path"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ],
- [
- "!in",
- "type",
- "crossing",
- "sidewalk",
- "steps"
- ]
- ]
- ],
- "id": "road-path-bg",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-blur": 0,
- "line-color": "hsl(230, 17%, 82%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 0.75
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 2
- ],
- [
- 18,
- 7
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ],
- [
- "==",
- "type",
- "steps"
- ]
- ]
- ],
- "id": "road-steps-bg",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(230, 17%, 82%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 0.75
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 2
- ],
- [
- 17,
- 4.6
- ],
- [
- 18,
- 7
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ],
- [
- "in",
- "type",
- "crossing",
- "sidewalk"
- ]
- ]
- ],
- "id": "road-sidewalk-bg",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 16,
- "paint": {
- "line-blur": 0,
- "line-color": "hsl(230, 17%, 82%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 16,
- 0
- ],
- [
- 16.25,
- 0.75
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 2
- ],
- [
- 18,
- 7
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "Point"
- ],
- [
- "in",
- "class",
- "turning_circle",
- "turning_loop"
- ]
- ],
- "id": "turning-features-outline",
- "layout": {
- "icon-allow-overlap": true,
- "icon-ignore-placement": true,
- "icon-image": "turning-circle-outline",
- "icon-padding": 0,
- "icon-rotation-alignment": "map",
- "icon-size": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.122
- ],
- [
- 18,
- 0.969
- ],
- [
- 20,
- 1
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 15,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "pedestrian"
- ],
- [
- "==",
- "structure",
- "none"
- ]
- ]
- ],
- "id": "road-pedestrian-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 12,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": 0,
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 2
- ],
- [
- 18,
- 14.5
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "street"
- ],
- [
- "==",
- "structure",
- "none"
- ]
- ]
- ],
- "id": "road-street-low",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "stops": [
- [
- 11,
- 0
- ],
- [
- 11.25,
- 1
- ],
- [
- 14,
- 1
- ],
- [
- 14.01,
- 0
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "street_limited"
- ],
- [
- "==",
- "structure",
- "none"
- ]
- ]
- ],
- "id": "road-street_limited-low",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "stops": [
- [
- 11,
- 0
- ],
- [
- 11.25,
- 1
- ],
- [
- 14,
- 1
- ],
- [
- 14.01,
- 0
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "service",
- "track"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ],
- [
- "!=",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "road-service-link-track-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 14,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "road-street_limited-case",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 13,
- 0
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "ref": "road-street_limited-low"
- },
- {
- "id": "road-street-case",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 13,
- 0
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "ref": "road-street-low"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "secondary",
- "tertiary"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ]
- ]
- ],
- "id": "road-secondary-tertiary-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 8.5,
- 0.5
- ],
- [
- 10,
- 0.75
- ],
- [
- 18,
- 26
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 9.99,
- 0
- ],
- [
- 10,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.2,
- "stops": [
- [
- 10,
- 0.75
- ],
- [
- 18,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "primary"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ]
- ]
- ],
- "id": "road-primary-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 9.99,
- 0
- ],
- [
- 10,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway_link"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ]
- ]
- ],
- "id": "road-motorway_link-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 10,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 10.99,
- 0
- ],
- [
- 11,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ],
- [
- "==",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "road-trunk_link-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 10.99,
- 0
- ],
- [
- 11,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "trunk"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ]
- ]
- ],
- "id": "road-trunk-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 6,
- 0
- ],
- [
- 6.1,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ]
- ]
- ],
- "id": "road-motorway-case",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "construction"
- ],
- [
- "==",
- "structure",
- "none"
- ]
- ]
- ],
- "id": "road-construction",
- "layout": {
- "line-join": "miter"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 14,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 0.4,
- 0.8
- ]
- ],
- [
- 15,
- [
- 0.3,
- 0.6
- ]
- ],
- [
- 16,
- [
- 0.2,
- 0.3
- ]
- ],
- [
- 17,
- [
- 0.2,
- 0.25
- ]
- ],
- [
- 18,
- [
- 0.15,
- 0.15
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "road-sidewalks",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.75,
- 1
- ]
- ],
- [
- 16,
- [
- 1,
- 0.75
- ]
- ],
- [
- 17,
- [
- 1,
- 0.5
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 16,
- 0
- ],
- [
- 16.25,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 1
- ],
- [
- 18,
- 4
- ]
- ]
- }
- },
- "ref": "road-sidewalk-bg"
- },
- {
- "id": "road-path",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.75,
- 1
- ]
- ],
- [
- 16,
- [
- 1,
- 0.75
- ]
- ],
- [
- 17,
- [
- 1,
- 0.5
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 1
- ],
- [
- 18,
- 4
- ]
- ]
- }
- },
- "ref": "road-path-bg"
- },
- {
- "id": "road-steps",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.75,
- 1
- ]
- ],
- [
- 16,
- [
- 1,
- 0.75
- ]
- ],
- [
- 17,
- [
- 0.3,
- 0.3
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 1
- ],
- [
- 16,
- 1.6
- ],
- [
- 18,
- 6
- ]
- ]
- }
- },
- "ref": "road-steps-bg"
- },
- {
- "id": "road-trunk_link",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(46, 85%, 67%)",
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "road-trunk_link-case"
- },
- {
- "id": "road-motorway_link",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(26, 100%, 68%)",
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "road-motorway_link-case"
- },
- {
- "id": "road-pedestrian",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.5,
- 0.4
- ]
- ],
- [
- 16,
- [
- 1,
- 0.2
- ]
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- }
- },
- "ref": "road-pedestrian-case"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "Polygon"
- ],
- [
- "all",
- [
- "in",
- "class",
- "path",
- "pedestrian"
- ],
- [
- "==",
- "structure",
- "none"
- ]
- ]
- ],
- "id": "road-pedestrian-polygon-fill",
- "layout": {},
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 12,
- "paint": {
- "fill-color": {
- "base": 1,
- "stops": [
- [
- 16,
- "hsl(230, 16%, 94%)"
- ],
- [
- 16.25,
- "hsl(230, 50%, 98%)"
- ]
- ]
- },
- "fill-opacity": 1,
- "fill-outline-color": "hsl(230, 26%, 88%)"
- },
- "source": "composite",
- "source-layer": "road",
- "type": "fill"
- },
- {
- "id": "road-pedestrian-polygon-pattern",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "fill-color": "hsl(0, 0%, 100%)",
- "fill-opacity": {
- "base": 1,
- "stops": [
- [
- 16,
- 0
- ],
- [
- 16.25,
- 1
- ]
- ]
- },
- "fill-outline-color": "hsl(35, 10%, 83%)",
- "fill-pattern": "pedestrian-polygon"
- },
- "ref": "road-pedestrian-polygon-fill"
- },
- {
- "id": "road-service-link-track",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- }
- },
- "ref": "road-service-link-track-case"
- },
- {
- "id": "road-street_limited",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(35, 14%, 93%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "road-street_limited-low"
- },
- {
- "id": "road-street",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "road-street-low"
- },
- {
- "id": "road-secondary-tertiary",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 5,
- "hsl(35, 32%, 91%)"
- ],
- [
- 8,
- "hsl(0, 0%, 100%)"
- ]
- ]
- },
- "line-opacity": {
- "base": 1.2,
- "stops": [
- [
- 5,
- 0
- ],
- [
- 5.5,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 8.5,
- 0.5
- ],
- [
- 10,
- 0.75
- ],
- [
- 18,
- 26
- ]
- ]
- }
- },
- "ref": "road-secondary-tertiary-case"
- },
- {
- "id": "road-primary",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 5,
- "hsl(35, 32%, 91%)"
- ],
- [
- 7,
- "hsl(0, 0%, 100%)"
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "ref": "road-primary-case"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "path",
- "pedestrian",
- "service",
- "track"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ],
- [
- "!=",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "road-oneway-arrows-blue-minor",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 17,
- "oneway-small"
- ],
- [
- 18,
- "oneway-large"
- ]
- ]
- },
- "icon-padding": 2,
- "icon-rotation-alignment": "map",
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 16,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "primary",
- "secondary",
- "street",
- "street_limited",
- "tertiary"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ],
- [
- "!=",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "road-oneway-arrows-blue-major",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 16,
- "oneway-small"
- ],
- [
- 17,
- "oneway-large"
- ]
- ]
- },
- "icon-padding": 2,
- "icon-rotation-alignment": "map",
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 15,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "id": "road-trunk",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 6,
- "hsl(0, 0%, 100%)"
- ],
- [
- 6.1,
- "hsl(46, 80%, 60%)"
- ],
- [
- 9,
- "hsl(46, 85%, 67%)"
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "ref": "road-trunk-case"
- },
- {
- "id": "road-motorway",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 8,
- "hsl(26, 87%, 62%)"
- ],
- [
- 9,
- "hsl(26, 100%, 68%)"
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "ref": "road-motorway-case"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "major_rail",
- "minor_rail"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ]
- ]
- ],
- "id": "road-rail",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 13,
- "paint": {
- "line-color": {
- "stops": [
- [
- 13,
- "hsl(50, 17%, 82%)"
- ],
- [
- 16,
- "hsl(230, 10%, 74%)"
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 20,
- 1
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "road-rail-tracks",
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "paint": {
- "line-color": {
- "stops": [
- [
- 13,
- "hsl(50, 17%, 82%)"
- ],
- [
- 16,
- "hsl(230, 10%, 74%)"
- ]
- ]
- },
- "line-dasharray": [
- 0.1,
- 15
- ],
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.75,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 4
- ],
- [
- 20,
- 8
- ]
- ]
- }
- },
- "ref": "road-rail"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "motorway",
- "motorway_link",
- "trunk"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "!in",
- "structure",
- "bridge",
- "tunnel"
- ],
- [
- "!in",
- "type",
- "primary_link",
- "secondary_link",
- "tertiary_link"
- ]
- ]
- ],
- "id": "road-oneway-arrows-white",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 16,
- "oneway-white-small"
- ],
- [
- 17,
- "oneway-white-large"
- ]
- ]
- },
- "icon-padding": 2,
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 16,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "Point"
- ],
- [
- "in",
- "class",
- "turning_circle",
- "turning_loop"
- ]
- ],
- "id": "turning-features",
- "layout": {
- "icon-allow-overlap": true,
- "icon-ignore-placement": true,
- "icon-image": "turning-circle",
- "icon-padding": 0,
- "icon-rotation-alignment": "map",
- "icon-size": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.095
- ],
- [
- 18,
- 1
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444855786460.0557"
- },
- "minzoom": 15,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "path"
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "!=",
- "type",
- "steps"
- ]
- ]
- ],
- "id": "bridge-path-bg",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-blur": 0,
- "line-color": "hsl(230, 17%, 82%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 15,
- 0
- ],
- [
- 15.25,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 2
- ],
- [
- 18,
- 7
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "==",
- "type",
- "steps"
- ]
- ]
- ],
- "id": "bridge-steps-bg",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(230, 17%, 82%)",
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 0.75
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 2
- ],
- [
- 17,
- 4.6
- ],
- [
- 18,
- 7
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "pedestrian"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-pedestrian-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": 0,
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 2
- ],
- [
- 18,
- 14.5
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "street"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-street-low",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "stops": [
- [
- 11.5,
- 0
- ],
- [
- 12,
- 1
- ],
- [
- 14,
- 1
- ],
- [
- 14.01,
- 0
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "street_limited"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-street_limited-low",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "stops": [
- [
- 11.5,
- 0
- ],
- [
- 12,
- 1
- ],
- [
- 14,
- 1
- ],
- [
- 14.01,
- 0
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "service",
- "track"
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "!=",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "bridge-service-link-track-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 14,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "street_limited"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-street_limited-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 13,
- 0
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "street"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-street-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 11,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 13,
- 0
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "secondary",
- "tertiary"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-secondary-tertiary-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 8.5,
- 0.5
- ],
- [
- 10,
- 0.75
- ],
- [
- 18,
- 26
- ]
- ]
- },
- "line-translate": [
- 0,
- 0
- ],
- "line-width": {
- "base": 1.2,
- "stops": [
- [
- 10,
- 0.75
- ],
- [
- 18,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "primary"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-primary-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-translate": [
- 0,
- 0
- ],
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "!in",
- "layer",
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "==",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "bridge-trunk_link-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 10.99,
- 0
- ],
- [
- 11,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway_link"
- ],
- [
- "!in",
- "layer",
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-motorway_link-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "trunk"
- ],
- [
- "!in",
- "layer",
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-trunk-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway"
- ],
- [
- "!in",
- "layer",
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-motorway-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "construction"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-construction",
- "layout": {
- "line-join": "miter"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 14,
- "paint": {
- "line-color": "hsl(230, 24%, 87%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 0.4,
- 0.8
- ]
- ],
- [
- 15,
- [
- 0.3,
- 0.6
- ]
- ],
- [
- 16,
- [
- 0.2,
- 0.3
- ]
- ],
- [
- 17,
- [
- 0.2,
- 0.25
- ]
- ],
- [
- 18,
- [
- 0.15,
- 0.15
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "path"
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "!=",
- "type",
- "steps"
- ]
- ]
- ],
- "id": "bridge-path",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.75,
- 1
- ]
- ],
- [
- 16,
- [
- 1,
- 0.75
- ]
- ],
- [
- 17,
- [
- 1,
- 0.5
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 1
- ],
- [
- 18,
- 4
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "bridge-steps",
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.75,
- 1
- ]
- ],
- [
- 16,
- [
- 1,
- 0.75
- ]
- ],
- [
- 17,
- [
- 0.3,
- 0.3
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 14,
- 0
- ],
- [
- 14.25,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 15,
- 1
- ],
- [
- 16,
- 1.6
- ],
- [
- 18,
- 6
- ]
- ]
- }
- },
- "ref": "bridge-steps-bg"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "!in",
- "layer",
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "==",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "bridge-trunk_link",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(46, 85%, 67%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway_link"
- ],
- [
- "!in",
- "layer",
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-motorway_link",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(26, 100%, 68%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "bridge-pedestrian",
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 14,
- [
- 1,
- 0
- ]
- ],
- [
- 15,
- [
- 1.5,
- 0.4
- ]
- ],
- [
- 16,
- [
- 1,
- 0.2
- ]
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- }
- },
- "ref": "bridge-pedestrian-case"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "service",
- "track"
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "!=",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "bridge-service-link-track",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 14,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 18,
- 12
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "bridge-street_limited",
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(35, 14%, 93%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "bridge-street_limited-low"
- },
- {
- "id": "bridge-street",
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12.5,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "ref": "bridge-street-low"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "in",
- "type",
- "secondary",
- "tertiary"
- ]
- ]
- ],
- "id": "bridge-secondary-tertiary",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": {
- "base": 1.2,
- "stops": [
- [
- 5,
- 0
- ],
- [
- 5.5,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 8.5,
- 0.5
- ],
- [
- 10,
- 0.75
- ],
- [
- 18,
- 26
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "==",
- "type",
- "primary"
- ]
- ]
- ],
- "id": "bridge-primary",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "path",
- "pedestrian",
- "service",
- "track"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-oneway-arrows-blue-minor",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 17,
- "oneway-small"
- ],
- [
- 18,
- "oneway-large"
- ]
- ]
- },
- "icon-padding": 2,
- "icon-rotation-alignment": "map",
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 16,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "primary",
- "secondary",
- "street",
- "street_limited",
- "tertiary"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-oneway-arrows-blue-major",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 16,
- "oneway-small"
- ],
- [
- 17,
- "oneway-large"
- ]
- ]
- },
- "icon-padding": 2,
- "icon-rotation-alignment": "map",
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 15,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "trunk"
- ],
- [
- "!in",
- "layer",
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-trunk",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(46, 85%, 67%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway"
- ],
- [
- "!in",
- "layer",
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-motorway",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(26, 100%, 68%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "major_rail",
- "minor_rail"
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-rail",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": {
- "stops": [
- [
- 13,
- "hsl(50, 17%, 82%)"
- ],
- [
- 16,
- "hsl(230, 10%, 74%)"
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 20,
- 1
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "id": "bridge-rail-tracks",
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": {
- "stops": [
- [
- 13,
- "hsl(50, 17%, 82%)"
- ],
- [
- 16,
- "hsl(230, 10%, 74%)"
- ]
- ]
- },
- "line-dasharray": [
- 0.1,
- 15
- ],
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 13.75,
- 0
- ],
- [
- 20,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 4
- ],
- [
- 20,
- 8
- ]
- ]
- }
- },
- "ref": "bridge-rail"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- ">=",
- "layer",
- 2
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "==",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "bridge-trunk_link-2-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 10.99,
- 0
- ],
- [
- 11,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway_link"
- ],
- [
- ">=",
- "layer",
- 2
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-motorway_link-2-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- },
- "line-opacity": 1,
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.75
- ],
- [
- 20,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "trunk"
- ],
- [
- ">=",
- "layer",
- 2
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-trunk-2-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway"
- ],
- [
- ">=",
- "layer",
- 2
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-motorway-2-case",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(0, 0%, 100%)",
- "line-gap-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- },
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 10,
- 1
- ],
- [
- 16,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- ">=",
- "layer",
- 2
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "==",
- "type",
- "trunk_link"
- ]
- ]
- ],
- "id": "bridge-trunk_link-2",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(46, 85%, 67%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway_link"
- ],
- [
- ">=",
- "layer",
- 2
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-motorway_link-2",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(26, 100%, 68%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 12,
- 0.5
- ],
- [
- 14,
- 2
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "trunk"
- ],
- [
- ">=",
- "layer",
- 2
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-trunk-2",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(46, 85%, 67%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "==",
- "class",
- "motorway"
- ],
- [
- ">=",
- "layer",
- 2
- ],
- [
- "==",
- "structure",
- "bridge"
- ]
- ]
- ],
- "id": "bridge-motorway-2",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "paint": {
- "line-color": "hsl(26, 100%, 68%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 5,
- 0.75
- ],
- [
- 18,
- 32
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "all",
- [
- "in",
- "class",
- "link",
- "motorway",
- "motorway_link",
- "trunk"
- ],
- [
- "==",
- "oneway",
- "true"
- ],
- [
- "==",
- "structure",
- "bridge"
- ],
- [
- "!in",
- "type",
- "primary_link",
- "secondary_link",
- "tertiary_link"
- ]
- ]
- ],
- "id": "bridge-oneway-arrows-white",
- "layout": {
- "icon-image": {
- "base": 1,
- "stops": [
- [
- 16,
- "oneway-white-small"
- ],
- [
- 17,
- "oneway-white-large"
- ]
- ]
- },
- "icon-padding": 2,
- "symbol-placement": "line",
- "symbol-spacing": 200
- },
- "metadata": {
- "mapbox:group": "1444855799204.86"
- },
- "minzoom": 16,
- "paint": {},
- "source": "composite",
- "source-layer": "road",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "==",
- "class",
- "aerialway"
- ]
- ],
- "id": "aerialway",
- "layout": {
- "line-join": "round"
- },
- "minzoom": 13,
- "paint": {
- "line-color": "hsl(230, 10%, 74%)",
- "line-width": {
- "base": 1.5,
- "stops": [
- [
- 14,
- 0.5
- ],
- [
- 20,
- 1
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "road",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- ">=",
- "admin_level",
- 3
- ],
- [
- "==",
- "maritime",
- 0
- ]
- ],
- "id": "admin-3-4-boundaries-bg",
- "layout": {
- "line-join": "bevel"
- },
- "metadata": {
- "mapbox:group": "1444934295202.7542"
- },
- "paint": {
- "line-blur": {
- "base": 1,
- "stops": [
- [
- 3,
- 0
- ],
- [
- 8,
- 3
- ]
- ]
- },
- "line-color": {
- "base": 1,
- "stops": [
- [
- 8,
- "hsl(35, 12%, 89%)"
- ],
- [
- 16,
- "hsl(230, 49%, 90%)"
- ]
- ]
- },
- "line-dasharray": [
- 1,
- 0
- ],
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 7,
- 0
- ],
- [
- 8,
- 0.75
- ]
- ]
- },
- "line-translate": [
- 0,
- 0
- ],
- "line-width": {
- "base": 1,
- "stops": [
- [
- 7,
- 3.75
- ],
- [
- 12,
- 5.5
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "admin",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "admin_level",
- 2
- ],
- [
- "==",
- "maritime",
- 0
- ]
- ],
- "id": "admin-2-boundaries-bg",
- "layout": {
- "line-join": "miter"
- },
- "metadata": {
- "mapbox:group": "1444934295202.7542"
- },
- "minzoom": 1,
- "paint": {
- "line-blur": {
- "base": 1,
- "stops": [
- [
- 3,
- 0
- ],
- [
- 10,
- 2
- ]
- ]
- },
- "line-color": {
- "base": 1,
- "stops": [
- [
- 6,
- "hsl(35, 12%, 89%)"
- ],
- [
- 8,
- "hsl(230, 49%, 90%)"
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 3,
- 0
- ],
- [
- 4,
- 0.5
- ]
- ]
- },
- "line-translate": [
- 0,
- 0
- ],
- "line-width": {
- "base": 1,
- "stops": [
- [
- 3,
- 3.5
- ],
- [
- 10,
- 8
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "admin",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- ">=",
- "admin_level",
- 3
- ],
- [
- "==",
- "maritime",
- 0
- ]
- ],
- "id": "admin-3-4-boundaries",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444934295202.7542"
- },
- "paint": {
- "line-color": {
- "base": 1,
- "stops": [
- [
- 3,
- "hsl(230, 14%, 77%)"
- ],
- [
- 7,
- "hsl(230, 8%, 62%)"
- ]
- ]
- },
- "line-dasharray": {
- "base": 1,
- "stops": [
- [
- 6,
- [
- 2,
- 0
- ]
- ],
- [
- 7,
- [
- 2,
- 2,
- 6,
- 2
- ]
- ]
- ]
- },
- "line-opacity": {
- "base": 1,
- "stops": [
- [
- 2,
- 0
- ],
- [
- 3,
- 1
- ]
- ]
- },
- "line-width": {
- "base": 1,
- "stops": [
- [
- 7,
- 0.75
- ],
- [
- 12,
- 1.5
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "admin",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "admin_level",
- 2
- ],
- [
- "==",
- "disputed",
- 0
- ],
- [
- "==",
- "maritime",
- 0
- ]
- ],
- "id": "admin-2-boundaries",
- "layout": {
- "line-cap": "round",
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444934295202.7542"
- },
- "minzoom": 1,
- "paint": {
- "line-color": "hsl(230, 8%, 51%)",
- "line-width": {
- "base": 1,
- "stops": [
- [
- 3,
- 0.5
- ],
- [
- 10,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "admin",
- "type": "line"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "admin_level",
- 2
- ],
- [
- "==",
- "disputed",
- 1
- ],
- [
- "==",
- "maritime",
- 0
- ]
- ],
- "id": "admin-2-boundaries-dispute",
- "layout": {
- "line-join": "round"
- },
- "metadata": {
- "mapbox:group": "1444934295202.7542"
- },
- "minzoom": 1,
- "paint": {
- "line-color": "hsl(230, 8%, 51%)",
- "line-dasharray": [
- 1.5,
- 1.5
- ],
- "line-width": {
- "base": 1,
- "stops": [
- [
- 3,
- 0.5
- ],
- [
- 10,
- 2
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "admin",
- "type": "line"
- },
- {
- "id": "housenum-label",
- "layout": {
- "text-field": "{house_num}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-max-width": 7,
- "text-padding": 4,
- "text-size": 9.5
- },
- "minzoom": 17,
- "paint": {
- "text-color": "hsl(35, 2%, 69%)",
- "text-halo-blur": 0,
- "text-halo-color": "hsl(35, 8%, 85%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "housenum_label",
- "type": "symbol"
- },
- {
- "filter": [
- "in",
- "class",
- "canal",
- "river"
- ],
- "id": "waterway-label",
- "layout": {
- "symbol-placement": "line",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-max-angle": 30,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 13,
- 12
- ],
- [
- 18,
- 16
- ]
- ]
- }
- },
- "minzoom": 12,
- "paint": {
- "text-color": "hsl(230, 48%, 44%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(196, 80%, 70%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "waterway_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- ">=",
- "localrank",
- 15
- ],
- [
- "!in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "==",
- "scalerank",
- 4
- ]
- ],
- "id": "poi-scalerank4-l15",
- "layout": {
- "icon-image": "{maki}-11",
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 16,
- 11
- ],
- [
- 20,
- 13
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933456003.5437"
- },
- "minzoom": 17,
- "paint": {
- "text-color": "hsl(26, 25%, 32%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "<=",
- "localrank",
- 14
- ],
- [
- "!in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "==",
- "scalerank",
- 4
- ]
- ],
- "id": "poi-scalerank4-l1",
- "layout": {
- "icon-image": "{maki}-11",
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 1,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 16,
- 11
- ],
- [
- 20,
- 13
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933456003.5437"
- },
- "minzoom": 15,
- "paint": {
- "text-color": "hsl(26, 25%, 32%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "==",
- "scalerank",
- 4
- ]
- ],
- "id": "poi-parks_scalerank4",
- "layout": {
- "icon-image": "{maki}-11",
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 1,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 16,
- 11
- ],
- [
- 20,
- 13
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933456003.5437"
- },
- "minzoom": 15,
- "paint": {
- "text-color": "hsl(100, 100%, 20%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "!in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "==",
- "scalerank",
- 3
- ]
- ],
- "id": "poi-scalerank3",
- "layout": {
- "icon-image": "{maki}-11",
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 1,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 16,
- 11
- ],
- [
- 20,
- 13
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933372896.5967"
- },
- "paint": {
- "text-color": "hsl(26, 25%, 32%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "==",
- "scalerank",
- 3
- ]
- ],
- "id": "poi-parks-scalerank3",
- "layout": {
- "icon-image": "{maki}-11",
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 16,
- 11
- ],
- [
- 20,
- 13
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933372896.5967"
- },
- "paint": {
- "text-color": "hsl(100, 100%, 20%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "!in",
- "class",
- "link",
- "motorway",
- "pedestrian",
- "primary",
- "secondary",
- "street",
- "street_limited",
- "tertiary",
- "trunk"
- ]
- ],
- "id": "road-label-small",
- "layout": {
- "symbol-placement": "line",
- "symbol-spacing": 250,
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-max-angle": 30,
- "text-padding": 1,
- "text-rotation-alignment": "map",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 15,
- 10
- ],
- [
- 20,
- 13
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933721429.3076"
- },
- "minzoom": 15,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-blur": 1,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1.25
- },
- "source": "composite",
- "source-layer": "road_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "in",
- "class",
- "link",
- "pedestrian",
- "street",
- "street_limited"
- ]
- ],
- "id": "road-label-medium",
- "layout": {
- "symbol-placement": "line",
- "symbol-spacing": 250,
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-max-angle": 30,
- "text-padding": 1,
- "text-rotation-alignment": "map",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 11,
- 10
- ],
- [
- 20,
- 14
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933721429.3076"
- },
- "minzoom": 11,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "road_label",
- "type": "symbol"
- },
- {
- "filter": [
- "in",
- "class",
- "motorway",
- "primary",
- "secondary",
- "tertiary",
- "trunk"
- ],
- "id": "road-label-large",
- "layout": {
- "symbol-placement": "line",
- "symbol-spacing": 250,
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-max-angle": 30,
- "text-padding": 1,
- "text-rotation-alignment": "map",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 9,
- 10
- ],
- [
- 20,
- 16
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933721429.3076"
- },
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-blur": 1,
- "text-halo-color": "hsla(0, 0%, 100%, 0.75)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "road_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "<=",
- "reflen",
- 6
- ],
- [
- "!in",
- "shield",
- "at-expressway",
- "at-motorway",
- "at-state-b",
- "bg-motorway",
- "bg-national",
- "ch-main",
- "ch-motorway",
- "cz-motorway",
- "cz-road",
- "de-motorway",
- "e-road",
- "fi-main",
- "gr-motorway",
- "gr-national",
- "hr-motorway",
- "hr-state",
- "hu-main",
- "hu-motorway",
- "nz-state",
- "pl-expressway",
- "pl-motorway",
- "pl-national",
- "ro-county",
- "ro-motorway",
- "ro-national",
- "rs-motorway",
- "rs-state-1b",
- "se-main",
- "si-expressway",
- "si-motorway",
- "sk-highway",
- "sk-road",
- "us-interstate",
- "us-interstate-business",
- "us-interstate-duplex",
- "us-interstate-truck",
- "za-metropolitan",
- "za-national",
- "za-provincial",
- "za-regional"
- ]
- ],
- "id": "road-shields-black",
- "layout": {
- "icon-image": "{shield}-{reflen}",
- "icon-padding": 2,
- "icon-rotation-alignment": "viewport",
- "symbol-placement": {
- "base": 1,
- "stops": [
- [
- 10,
- "point"
- ],
- [
- 11,
- "line"
- ]
- ]
- },
- "symbol-spacing": {
- "base": 1,
- "stops": [
- [
- 11,
- 150
- ],
- [
- 14,
- 200
- ]
- ]
- },
- "text-field": "{ref}",
- "text-font": [
- "DIN Offc Pro Bold",
- "Arial Unicode MS Bold"
- ],
- "text-letter-spacing": 0.05,
- "text-max-angle": 38,
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": 9
- },
- "metadata": {
- "mapbox:group": "1444933575858.6992"
- },
- "paint": {
- "icon-color": "white",
- "icon-halo-color": "rgba(0, 0, 0, 1)",
- "icon-halo-width": 1,
- "text-color": "hsl(0, 0%, 7%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0,
- "text-opacity": 1
- },
- "source": "composite",
- "source-layer": "road_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "<=",
- "reflen",
- 6
- ],
- [
- "in",
- "shield",
- "at-expressway",
- "at-motorway",
- "at-state-b",
- "bg-motorway",
- "bg-national",
- "ch-main",
- "ch-motorway",
- "cz-motorway",
- "cz-road",
- "de-motorway",
- "e-road",
- "fi-main",
- "gr-motorway",
- "gr-national",
- "hr-motorway",
- "hr-state",
- "hu-main",
- "hu-motorway",
- "nz-state",
- "pl-expressway",
- "pl-motorway",
- "pl-national",
- "ro-county",
- "ro-motorway",
- "ro-national",
- "rs-motorway",
- "rs-state-1b",
- "se-main",
- "si-expressway",
- "si-motorway",
- "sk-highway",
- "sk-road",
- "us-interstate",
- "us-interstate-business",
- "us-interstate-duplex",
- "us-interstate-truck",
- "za-metropolitan",
- "za-national",
- "za-provincial",
- "za-regional"
- ]
- ],
- "id": "road-shields-white",
- "layout": {
- "icon-image": "{shield}-{reflen}",
- "icon-padding": 2,
- "icon-rotation-alignment": "viewport",
- "symbol-placement": {
- "base": 1,
- "stops": [
- [
- 10,
- "point"
- ],
- [
- 11,
- "line"
- ]
- ]
- },
- "symbol-spacing": {
- "base": 1,
- "stops": [
- [
- 11,
- 150
- ],
- [
- 14,
- 200
- ]
- ]
- },
- "text-field": "{ref}",
- "text-font": [
- "DIN Offc Pro Bold",
- "Arial Unicode MS Bold"
- ],
- "text-letter-spacing": 0.05,
- "text-max-angle": 38,
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": 9
- },
- "metadata": {
- "mapbox:group": "1444933575858.6992"
- },
- "paint": {
- "icon-color": "white",
- "icon-halo-color": "rgba(0, 0, 0, 1)",
- "icon-halo-width": 1,
- "text-color": "hsl(0, 0%, 100%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0,
- "text-opacity": 1
- },
- "source": "composite",
- "source-layer": "road_label",
- "type": "symbol"
- },
- {
- "filter": [
- ">",
- "reflen",
- 0
- ],
- "id": "motorway-junction",
- "layout": {
- "icon-image": "motorway-exit-{reflen}",
- "text-field": "{ref}",
- "text-font": [
- "DIN Offc Pro Bold",
- "Arial Unicode MS Bold"
- ],
- "text-size": 9
- },
- "metadata": {
- "mapbox:group": "1444933575858.6992"
- },
- "minzoom": 14,
- "paint": {
- "text-color": "hsl(0, 0%, 100%)",
- "text-translate": [
- 0,
- 0
- ]
- },
- "source": "composite",
- "source-layer": "motorway_junction",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "!in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "==",
- "scalerank",
- 2
- ]
- ],
- "id": "poi-scalerank2",
- "layout": {
- "icon-image": {
- "stops": [
- [
- 14,
- "{maki}-11"
- ],
- [
- 15,
- "{maki}-15"
- ]
- ]
- },
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 14,
- 11
- ],
- [
- 20,
- 14
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933358918.2366"
- },
- "paint": {
- "text-color": "hsl(26, 25%, 32%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "==",
- "scalerank",
- 2
- ]
- ],
- "id": "poi-parks-scalerank2",
- "layout": {
- "icon-image": {
- "stops": [
- [
- 14,
- "{maki}-11"
- ],
- [
- 15,
- "{maki}-15"
- ]
- ]
- },
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 14,
- 11
- ],
- [
- 20,
- 14
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933358918.2366"
- },
- "paint": {
- "text-color": "hsl(100, 100%, 20%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "!=",
- "maki",
- "entrance"
- ],
- "id": "rail-label",
- "layout": {
- "icon-image": "{network}",
- "icon-padding": 0,
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": {
- "base": 1,
- "stops": [
- [
- 0,
- ""
- ],
- [
- 13,
- "{name_en}"
- ]
- ]
- },
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-width": 7,
- "text-offset": [
- 0,
- 0.85
- ],
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 16,
- 11
- ],
- [
- 20,
- 13
- ]
- ]
- }
- },
- "minzoom": 12,
- "paint": {
- "icon-halo-color": "#fff",
- "icon-halo-width": 4,
- "text-color": "hsl(230, 48%, 44%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5,
- "text-opacity": {
- "base": 1,
- "stops": [
- [
- 13.99,
- 0
- ],
- [
- 14,
- 1
- ]
- ]
- }
- },
- "source": "composite",
- "source-layer": "rail_station_label",
- "type": "symbol"
- },
- {
- "filter": [
- "<=",
- "area",
- 10000
- ],
- "id": "water-label-sm",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-max-width": 7,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 16,
- 13
- ],
- [
- 20,
- 16
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933808272.805"
- },
- "minzoom": 15,
- "paint": {
- "text-color": "hsl(230, 48%, 44%)"
- },
- "source": "composite",
- "source-layer": "water_label",
- "type": "symbol"
- },
- {
- "filter": [
- ">",
- "area",
- 10000
- ],
- "id": "water-label",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-max-width": 7,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 13,
- 13
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933808272.805"
- },
- "minzoom": 5,
- "paint": {
- "text-color": "hsl(230, 48%, 44%)"
- },
- "source": "composite",
- "source-layer": "water_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "$type",
- "LineString",
- "Point",
- "Polygon"
- ],
- [
- "all",
- [
- "<=",
- "localrank",
- 10
- ],
- [
- "==",
- "type",
- "residential"
- ]
- ]
- ],
- "id": "place-residential",
- "layout": {
- "symbol-spacing": 250,
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-line-height": 1.2,
- "text-max-angle": 38,
- "text-max-width": 7,
- "text-offset": [
- 0,
- 0
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 10,
- 11
- ],
- [
- 18,
- 14
- ]
- ]
- },
- "visibility": "none"
- },
- "maxzoom": 18,
- "minzoom": 16,
- "paint": {
- "text-color": "hsl(26, 25%, 32%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "<=",
- "scalerank",
- 1
- ]
- ],
- "id": "poi-parks-scalerank1",
- "layout": {
- "icon-image": {
- "stops": [
- [
- 13,
- "{maki}-11"
- ],
- [
- 14,
- "{maki}-15"
- ]
- ]
- },
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 10,
- 11
- ],
- [
- 18,
- 14
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933322393.2852"
- },
- "paint": {
- "text-color": "hsl(100, 100%, 20%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "!in",
- "maki",
- "campsite",
- "cemetery",
- "dog-park",
- "garden",
- "golf",
- "park",
- "picnic-site",
- "playground",
- "zoo"
- ],
- [
- "<=",
- "scalerank",
- 1
- ]
- ],
- "id": "poi-scalerank1",
- "layout": {
- "icon-image": {
- "stops": [
- [
- 13,
- "{maki}-11"
- ],
- [
- 14,
- "{maki}-15"
- ]
- ]
- },
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0.65
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 10,
- 11
- ],
- [
- 18,
- 14
- ]
- ]
- }
- },
- "metadata": {
- "mapbox:group": "1444933322393.2852"
- },
- "paint": {
- "text-color": "hsl(26, 25%, 32%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "poi_label",
- "type": "symbol"
- },
- {
- "filter": [
- "<=",
- "scalerank",
- 2
- ],
- "id": "airport-label",
- "layout": {
- "icon-image": {
- "stops": [
- [
- 12,
- "{maki}-11"
- ],
- [
- 13,
- "{maki}-15"
- ]
- ]
- },
- "symbol-spacing": 250,
- "text-anchor": "top",
- "text-field": {
- "stops": [
- [
- 11,
- "{ref}"
- ],
- [
- 12,
- "{name_en}"
- ]
- ]
- },
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.1,
- "text-max-width": 9,
- "text-offset": [
- 0,
- 0.75
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 10,
- 12
- ],
- [
- 18,
- 18
- ]
- ]
- }
- },
- "minzoom": 9,
- "paint": {
- "text-color": "hsl(230, 48%, 44%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 0.5
- },
- "source": "composite",
- "source-layer": "airport_label",
- "type": "symbol"
- },
- {
- "filter": [
- "in",
- "type",
- "aboriginal_lands",
- "archipelago",
- "islet"
- ],
- "id": "place-islet-archipelago-aboriginal",
- "layout": {
- "symbol-spacing": 250,
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.2,
- "text-max-angle": 38,
- "text-max-width": 8,
- "text-offset": [
- 0,
- 0
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 10,
- 11
- ],
- [
- 18,
- 16
- ]
- ]
- }
- },
- "maxzoom": 16,
- "paint": {
- "text-color": "hsl(230, 29%, 35%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "==",
- "type",
- "neighbourhood"
- ],
- "id": "place-neighbourhood",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.1,
- "text-max-width": 7,
- "text-padding": 3,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 12,
- 11
- ],
- [
- 16,
- 16
- ]
- ]
- },
- "text-transform": "uppercase"
- },
- "maxzoom": 16,
- "minzoom": 10,
- "paint": {
- "text-color": "hsl(230, 29%, 35%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "==",
- "type",
- "suburb"
- ],
- "id": "place-suburb",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.15,
- "text-max-width": 7,
- "text-padding": 3,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 11,
- 11
- ],
- [
- 15,
- 18
- ]
- ]
- },
- "text-transform": "uppercase"
- },
- "maxzoom": 16,
- "minzoom": 10,
- "paint": {
- "text-color": "hsl(230, 29%, 35%)",
- "text-halo-blur": 0.5,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "==",
- "type",
- "hamlet"
- ],
- "id": "place-hamlet",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-size": {
- "base": 1,
- "stops": [
- [
- 12,
- 11.5
- ],
- [
- 15,
- 16
- ]
- ]
- }
- },
- "maxzoom": 16,
- "minzoom": 10,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1.25
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "==",
- "type",
- "village"
- ],
- "id": "place-village",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-max-width": 7,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 10,
- 11.5
- ],
- [
- 16,
- 18
- ]
- ]
- }
- },
- "maxzoom": 15,
- "minzoom": 8,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1.25
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "==",
- "type",
- "town"
- ],
- "id": "place-town",
- "layout": {
- "icon-image": "dot-9",
- "text-anchor": {
- "base": 1,
- "stops": [
- [
- 7,
- "bottom"
- ],
- [
- 8,
- "center"
- ]
- ]
- },
- "text-field": "{name_en}",
- "text-font": {
- "base": 1,
- "stops": [
- [
- 11,
- [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ]
- ],
- [
- 12,
- [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ]
- ]
- ]
- },
- "text-max-width": 7,
- "text-offset": {
- "base": 1,
- "stops": [
- [
- 7,
- [
- 0,
- -0.15
- ]
- ],
- [
- 8,
- [
- 0,
- 0
- ]
- ]
- ]
- },
- "text-size": {
- "base": 1,
- "stops": [
- [
- 7,
- 11.5
- ],
- [
- 15,
- 20
- ]
- ]
- }
- },
- "maxzoom": 15,
- "minzoom": 6,
- "paint": {
- "icon-opacity": {
- "base": 1,
- "stops": [
- [
- 7.99,
- 1
- ],
- [
- 8,
- 0
- ]
- ]
- },
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1.25
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "==",
- "type",
- "island"
- ],
- "id": "place-island",
- "layout": {
- "symbol-spacing": 250,
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.01,
- "text-line-height": 1.2,
- "text-max-angle": 38,
- "text-max-width": 7,
- "text-offset": [
- 0,
- 0
- ],
- "text-padding": 2,
- "text-rotation-alignment": "viewport",
- "text-size": {
- "base": 1,
- "stops": [
- [
- 10,
- 11
- ],
- [
- 18,
- 16
- ]
- ]
- }
- },
- "maxzoom": 16,
- "paint": {
- "text-color": "hsl(230, 29%, 35%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "!in",
- "scalerank",
- 0,
- 1,
- 2,
- 3,
- 4,
- 5
- ],
- [
- "==",
- "type",
- "city"
- ]
- ],
- "id": "place-city-sm",
- "layout": {
- "icon-image": "dot-9",
- "text-anchor": {
- "base": 1,
- "stops": [
- [
- 7,
- "bottom"
- ],
- [
- 8,
- "center"
- ]
- ]
- },
- "text-field": "{name_en}",
- "text-font": {
- "base": 1,
- "stops": [
- [
- 7,
- [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ]
- ],
- [
- 8,
- [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ]
- ]
- ]
- },
- "text-max-width": 7,
- "text-offset": {
- "base": 1,
- "stops": [
- [
- 7.99,
- [
- 0,
- -0.2
- ]
- ],
- [
- 8,
- [
- 0,
- 0
- ]
- ]
- ]
- },
- "text-size": {
- "base": 1,
- "stops": [
- [
- 6,
- 12
- ],
- [
- 14,
- 22
- ]
- ]
- }
- },
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1444862510685.128"
- },
- "paint": {
- "icon-opacity": {
- "base": 1,
- "stops": [
- [
- 7.99,
- 1
- ],
- [
- 8,
- 0
- ]
- ]
- },
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1.25
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "ldir",
- "E",
- "S",
- "SE",
- "SW"
- ],
- [
- "in",
- "scalerank",
- 3,
- 4,
- 5
- ],
- [
- "==",
- "type",
- "city"
- ]
- ],
- "id": "place-city-md-s",
- "layout": {
- "icon-image": "dot-10",
- "text-anchor": {
- "base": 1,
- "stops": [
- [
- 7,
- "top"
- ],
- [
- 8,
- "center"
- ]
- ]
- },
- "text-field": "{name_en}",
- "text-font": {
- "base": 1,
- "stops": [
- [
- 7,
- [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ]
- ],
- [
- 8,
- [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ]
- ]
- ]
- },
- "text-offset": {
- "base": 1,
- "stops": [
- [
- 7.99,
- [
- 0,
- 0.1
- ]
- ],
- [
- 8,
- [
- 0,
- 0
- ]
- ]
- ]
- },
- "text-size": {
- "base": 0.9,
- "stops": [
- [
- 5,
- 12
- ],
- [
- 12,
- 22
- ]
- ]
- }
- },
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1444862510685.128"
- },
- "paint": {
- "icon-opacity": {
- "base": 1,
- "stops": [
- [
- 7.99,
- 1
- ],
- [
- 8,
- 0
- ]
- ]
- },
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-blur": 1,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "ldir",
- "N",
- "NE",
- "NW",
- "W"
- ],
- [
- "in",
- "scalerank",
- 3,
- 4,
- 5
- ],
- [
- "==",
- "type",
- "city"
- ]
- ],
- "id": "place-city-md-n",
- "layout": {
- "icon-image": "dot-10",
- "text-anchor": {
- "base": 1,
- "stops": [
- [
- 7,
- "bottom"
- ],
- [
- 8,
- "center"
- ]
- ]
- },
- "text-field": "{name_en}",
- "text-font": {
- "base": 1,
- "stops": [
- [
- 7,
- [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ]
- ],
- [
- 8,
- [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ]
- ]
- ]
- },
- "text-max-width": 7,
- "text-offset": {
- "base": 1,
- "stops": [
- [
- 7.99,
- [
- 0,
- -0.25
- ]
- ],
- [
- 8,
- [
- 0,
- 0
- ]
- ]
- ]
- },
- "text-size": {
- "base": 0.9,
- "stops": [
- [
- 5,
- 12
- ],
- [
- 12,
- 22
- ]
- ]
- }
- },
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1444862510685.128"
- },
- "paint": {
- "icon-opacity": {
- "base": 1,
- "stops": [
- [
- 7.99,
- 1
- ],
- [
- 8,
- 0
- ]
- ]
- },
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-blur": 1,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "ldir",
- "E",
- "S",
- "SE",
- "SW"
- ],
- [
- "<=",
- "scalerank",
- 2
- ],
- [
- "==",
- "type",
- "city"
- ]
- ],
- "id": "place-city-lg-s",
- "layout": {
- "icon-image": "dot-11",
- "text-anchor": {
- "base": 1,
- "stops": [
- [
- 7,
- "top"
- ],
- [
- 8,
- "center"
- ]
- ]
- },
- "text-field": "{name_en}",
- "text-font": {
- "base": 1,
- "stops": [
- [
- 7,
- [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ]
- ],
- [
- 8,
- [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ]
- ]
- ]
- },
- "text-max-width": 7,
- "text-offset": {
- "base": 1,
- "stops": [
- [
- 7.99,
- [
- 0,
- 0.15
- ]
- ],
- [
- 8,
- [
- 0,
- 0
- ]
- ]
- ]
- },
- "text-size": {
- "base": 0.9,
- "stops": [
- [
- 4,
- 12
- ],
- [
- 10,
- 22
- ]
- ]
- }
- },
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1444862510685.128"
- },
- "minzoom": 1,
- "paint": {
- "icon-opacity": {
- "base": 1,
- "stops": [
- [
- 7.99,
- 1
- ],
- [
- 8,
- 0
- ]
- ]
- },
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-blur": 1,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "in",
- "ldir",
- "N",
- "NE",
- "NW",
- "W"
- ],
- [
- "<=",
- "scalerank",
- 2
- ],
- [
- "==",
- "type",
- "city"
- ]
- ],
- "id": "place-city-lg-n",
- "layout": {
- "icon-image": "dot-11",
- "text-anchor": {
- "base": 1,
- "stops": [
- [
- 7,
- "bottom"
- ],
- [
- 8,
- "center"
- ]
- ]
- },
- "text-field": "{name_en}",
- "text-font": {
- "base": 1,
- "stops": [
- [
- 7,
- [
- "DIN Offc Pro Regular",
- "Arial Unicode MS Regular"
- ]
- ],
- [
- 8,
- [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ]
- ]
- ]
- },
- "text-max-width": 7,
- "text-offset": {
- "base": 1,
- "stops": [
- [
- 7.99,
- [
- 0,
- -0.25
- ]
- ],
- [
- 8,
- [
- 0,
- 0
- ]
- ]
- ]
- },
- "text-size": {
- "base": 0.9,
- "stops": [
- [
- 4,
- 12
- ],
- [
- 10,
- 22
- ]
- ]
- }
- },
- "maxzoom": 14,
- "metadata": {
- "mapbox:group": "1444862510685.128"
- },
- "minzoom": 1,
- "paint": {
- "icon-opacity": {
- "base": 1,
- "stops": [
- [
- 7.99,
- 1
- ],
- [
- 8,
- 0
- ]
- ]
- },
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-blur": 1,
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1,
- "text-opacity": 1
- },
- "source": "composite",
- "source-layer": "place_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- ">=",
- "labelrank",
- 4
- ]
- ],
- "id": "marine-label-sm-ln",
- "layout": {
- "symbol-placement": "line",
- "symbol-spacing": {
- "base": 1,
- "stops": [
- [
- 4,
- 100
- ],
- [
- 6,
- 400
- ]
- ]
- },
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.1,
- "text-line-height": 1.1,
- "text-max-width": 5,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 3,
- 12
- ],
- [
- 6,
- 16
- ]
- ]
- }
- },
- "maxzoom": 10,
- "metadata": {
- "mapbox:group": "1444856087950.3635"
- },
- "minzoom": 3,
- "paint": {
- "text-color": "hsl(205, 83%, 88%)"
- },
- "source": "composite",
- "source-layer": "marine_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "Point"
- ],
- [
- ">=",
- "labelrank",
- 4
- ]
- ],
- "id": "marine-label-sm-pt",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.1,
- "text-line-height": 1.5,
- "text-max-width": 5,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 3,
- 12
- ],
- [
- 6,
- 16
- ]
- ]
- }
- },
- "maxzoom": 10,
- "metadata": {
- "mapbox:group": "1444856087950.3635"
- },
- "minzoom": 3,
- "paint": {
- "text-color": "hsl(205, 83%, 88%)"
- },
- "source": "composite",
- "source-layer": "marine_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "in",
- "labelrank",
- 2,
- 3
- ]
- ],
- "id": "marine-label-md-ln",
- "layout": {
- "symbol-placement": "line",
- "symbol-spacing": 250,
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.15,
- "text-line-height": 1.1,
- "text-max-width": 5,
- "text-size": {
- "base": 1.1,
- "stops": [
- [
- 2,
- 12
- ],
- [
- 5,
- 20
- ]
- ]
- }
- },
- "maxzoom": 8,
- "metadata": {
- "mapbox:group": "1444856087950.3635"
- },
- "minzoom": 2,
- "paint": {
- "text-color": "hsl(205, 83%, 88%)"
- },
- "source": "composite",
- "source-layer": "marine_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "Point"
- ],
- [
- "in",
- "labelrank",
- 2,
- 3
- ]
- ],
- "id": "marine-label-md-pt",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.15,
- "text-line-height": 1.5,
- "text-max-width": 5,
- "text-size": {
- "base": 1.1,
- "stops": [
- [
- 2,
- 14
- ],
- [
- 5,
- 20
- ]
- ]
- }
- },
- "maxzoom": 8,
- "metadata": {
- "mapbox:group": "1444856087950.3635"
- },
- "minzoom": 2,
- "paint": {
- "text-color": "hsl(205, 83%, 88%)"
- },
- "source": "composite",
- "source-layer": "marine_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "LineString"
- ],
- [
- "==",
- "labelrank",
- 1
- ]
- ],
- "id": "marine-label-lg-ln",
- "layout": {
- "symbol-placement": "line",
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.25,
- "text-line-height": 1.1,
- "text-max-width": 4,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 1,
- 14
- ],
- [
- 4,
- 30
- ]
- ]
- }
- },
- "maxzoom": 4,
- "metadata": {
- "mapbox:group": "1444856087950.3635"
- },
- "minzoom": 1,
- "paint": {
- "text-color": "hsl(205, 83%, 88%)"
- },
- "source": "composite",
- "source-layer": "marine_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "==",
- "$type",
- "Point"
- ],
- [
- "==",
- "labelrank",
- 1
- ]
- ],
- "id": "marine-label-lg-pt",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Italic",
- "Arial Unicode MS Regular"
- ],
- "text-letter-spacing": 0.25,
- "text-line-height": 1.5,
- "text-max-width": 4,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 1,
- 14
- ],
- [
- 4,
- 30
- ]
- ]
- }
- },
- "maxzoom": 4,
- "metadata": {
- "mapbox:group": "1444856087950.3635"
- },
- "minzoom": 1,
- "paint": {
- "text-color": "hsl(205, 83%, 88%)"
- },
- "source": "composite",
- "source-layer": "marine_label",
- "type": "symbol"
- },
- {
- "filter": [
- "<",
- "area",
- 20000
- ],
- "id": "state-label-sm",
- "layout": {
- "text-field": {
- "base": 1,
- "stops": [
- [
- 0,
- "{abbr}"
- ],
- [
- 6,
- "{name_en}"
- ]
- ]
- },
- "text-font": [
- "DIN Offc Pro Bold",
- "Arial Unicode MS Bold"
- ],
- "text-letter-spacing": 0.15,
- "text-max-width": 5,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 6,
- 10
- ],
- [
- 9,
- 14
- ]
- ]
- },
- "text-transform": "uppercase"
- },
- "maxzoom": 9,
- "metadata": {
- "mapbox:group": "1444856151690.9143"
- },
- "minzoom": 3,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1,
- "text-opacity": 1
- },
- "source": "composite",
- "source-layer": "state_label",
- "type": "symbol"
- },
- {
- "filter": [
- "all",
- [
- "<",
- "area",
- 80000
- ],
- [
- ">=",
- "area",
- 20000
- ]
- ],
- "id": "state-label-md",
- "layout": {
- "text-field": {
- "base": 1,
- "stops": [
- [
- 0,
- "{abbr}"
- ],
- [
- 5,
- "{name_en}"
- ]
- ]
- },
- "text-font": [
- "DIN Offc Pro Bold",
- "Arial Unicode MS Bold"
- ],
- "text-letter-spacing": 0.15,
- "text-max-width": 6,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 5,
- 10
- ],
- [
- 8,
- 16
- ]
- ]
- },
- "text-transform": "uppercase"
- },
- "maxzoom": 8,
- "metadata": {
- "mapbox:group": "1444856151690.9143"
- },
- "minzoom": 3,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1,
- "text-opacity": 1
- },
- "source": "composite",
- "source-layer": "state_label",
- "type": "symbol"
- },
- {
- "filter": [
- ">=",
- "area",
- 80000
- ],
- "id": "state-label-lg",
- "layout": {
- "text-field": {
- "base": 1,
- "stops": [
- [
- 0,
- "{abbr}"
- ],
- [
- 4,
- "{name_en}"
- ]
- ]
- },
- "text-font": [
- "DIN Offc Pro Bold",
- "Arial Unicode MS Bold"
- ],
- "text-letter-spacing": 0.15,
- "text-max-width": 6,
- "text-padding": 1,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 4,
- 10
- ],
- [
- 7,
- 18
- ]
- ]
- },
- "text-transform": "uppercase"
- },
- "maxzoom": 7,
- "metadata": {
- "mapbox:group": "1444856151690.9143"
- },
- "minzoom": 3,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": "hsl(0, 0%, 100%)",
- "text-halo-width": 1,
- "text-opacity": 1
- },
- "source": "composite",
- "source-layer": "state_label",
- "type": "symbol"
- },
- {
- "filter": [
- ">=",
- "scalerank",
- 5
- ],
- "id": "country-label-sm",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-max-width": 6,
- "text-size": {
- "base": 0.9,
- "stops": [
- [
- 5,
- 14
- ],
- [
- 9,
- 22
- ]
- ]
- }
- },
- "maxzoom": 10,
- "metadata": {
- "mapbox:group": "1444856144497.7825"
- },
- "minzoom": 1,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": {
- "base": 1,
- "stops": [
- [
- 2,
- "rgba(255,255,255,0.75)"
- ],
- [
- 3,
- "hsl(0, 0%, 100%)"
- ]
- ]
- },
- "text-halo-width": 1.25
- },
- "source": "composite",
- "source-layer": "country_label",
- "type": "symbol"
- },
- {
- "filter": [
- "in",
- "scalerank",
- 3,
- 4
- ],
- "id": "country-label-md",
- "layout": {
- "text-field": {
- "base": 1,
- "stops": [
- [
- 0,
- "{code}"
- ],
- [
- 2,
- "{name_en}"
- ]
- ]
- },
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-max-width": 6,
- "text-size": {
- "base": 1,
- "stops": [
- [
- 3,
- 10
- ],
- [
- 8,
- 24
- ]
- ]
- }
- },
- "maxzoom": 8,
- "metadata": {
- "mapbox:group": "1444856144497.7825"
- },
- "minzoom": 1,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": {
- "base": 1,
- "stops": [
- [
- 2,
- "rgba(255,255,255,0.75)"
- ],
- [
- 3,
- "hsl(0, 0%, 100%)"
- ]
- ]
- },
- "text-halo-width": 1.25
- },
- "source": "composite",
- "source-layer": "country_label",
- "type": "symbol"
- },
- {
- "filter": [
- "in",
- "scalerank",
- 1,
- 2
- ],
- "id": "country-label-lg",
- "layout": {
- "text-field": "{name_en}",
- "text-font": [
- "DIN Offc Pro Medium",
- "Arial Unicode MS Regular"
- ],
- "text-max-width": {
- "base": 1,
- "stops": [
- [
- 0,
- 5
- ],
- [
- 3,
- 6
- ]
- ]
- },
- "text-size": {
- "base": 1,
- "stops": [
- [
- 1,
- 10
- ],
- [
- 6,
- 24
- ]
- ]
- }
- },
- "maxzoom": 7,
- "metadata": {
- "mapbox:group": "1444856144497.7825"
- },
- "minzoom": 1,
- "paint": {
- "text-color": "hsl(0, 0%, 0%)",
- "text-halo-color": {
- "base": 1,
- "stops": [
- [
- 2,
- "rgba(255,255,255,0.75)"
- ],
- [
- 3,
- "hsl(0, 0%, 100%)"
- ]
- ]
- },
- "text-halo-width": 1.25
- },
- "source": "composite",
- "source-layer": "country_label",
- "type": "symbol"
- },
- {
- "id": "testlayer",
- "type": "symbol",
- "source": "testsource",
- "layout": {
- "icon-image": "test-icon"
- }
- }
- ],
- "metadata": {
- "mapbox:autocomposite": true,
- "mapbox:groups": {
- "1444855769305.6016": {
- "collapsed": true,
- "name": "Tunnels"
- },
- "1444855786460.0557": {
- "collapsed": true,
- "name": "Roads"
- },
- "1444855799204.86": {
- "collapsed": true,
- "name": "Bridges"
- },
- "1444856087950.3635": {
- "collapsed": true,
- "name": "Marine labels"
- },
- "1444856144497.7825": {
- "collapsed": true,
- "name": "Country labels"
- },
- "1444856151690.9143": {
- "collapsed": true,
- "name": "State labels"
- },
- "1444862510685.128": {
- "collapsed": true,
- "name": "City labels"
- },
- "1444933322393.2852": {
- "collapsed": true,
- "name": "POI labels (scalerank 1)"
- },
- "1444933358918.2366": {
- "collapsed": true,
- "name": "POI labels (scalerank 2)"
- },
- "1444933372896.5967": {
- "collapsed": true,
- "name": "POI labels (scalerank 3)"
- },
- "1444933456003.5437": {
- "collapsed": true,
- "name": "POI labels (scalerank 4)"
- },
- "1444933575858.6992": {
- "collapsed": true,
- "name": "Highway shields"
- },
- "1444933721429.3076": {
- "collapsed": true,
- "name": "Road labels"
- },
- "1444933808272.805": {
- "collapsed": true,
- "name": "Water labels"
- },
- "1444934295202.7542": {
- "collapsed": true,
- "name": "Admin boundaries"
- },
- "1444934828655.3389": {
- "collapsed": true,
- "name": "Aeroways"
- },
- "1456969573402.7817": {
- "collapsed": true,
- "name": "Hillshading"
- },
- "1456970288113.8113": {
- "collapsed": true,
- "name": "Landcover"
- }
- },
- "mapbox:type": "default"
- },
- "modified": 0,
- "name": "Mapbox Streets",
- "owner": "mapbox",
- "sources": {
- "composite": {
- "type": "vector",
- "url": "mapbox://mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7"
- },
- "testsource": {
- "type": "geojson",
- "data": {
- "type": "Point",
- "coordinates": [
- -73.992857,
- 40.726989
- ]
- }
- }
- },
- "sprite": "mapbox://sprites/mapbox/streets-v9",
- "version": 8
-}
diff --git a/benchmark/fixtures/api/style.json b/benchmark/fixtures/api/style.json
new file mode 100644
index 0000000000..cf88aeb6ed
--- /dev/null
+++ b/benchmark/fixtures/api/style.json
@@ -0,0 +1,3993 @@
+{
+ "version": 8,
+ "name": "Mapbox Streets",
+ "metadata": {
+ "mapbox:autocomposite": true,
+ "mapbox:type": "default",
+ "mapbox:groups": {
+ "1444934828655.3389": { "name": "Aeroways", "collapsed": true },
+ "1444933322393.2852": { "name": "POI labels (scalerank 1)", "collapsed": true },
+ "1444855786460.0557": { "name": "Roads", "collapsed": true },
+ "1444933575858.6992": { "name": "Highway shields", "collapsed": true },
+ "1444934295202.7542": { "name": "Admin boundaries", "collapsed": true },
+ "1444856151690.9143": { "name": "State labels", "collapsed": true },
+ "1444933721429.3076": { "name": "Road labels", "collapsed": true },
+ "1444933358918.2366": { "name": "POI labels (scalerank 2)", "collapsed": true },
+ "1444933808272.805": { "name": "Water labels", "collapsed": true },
+ "1444933372896.5967": { "name": "POI labels (scalerank 3)", "collapsed": true },
+ "1444855799204.86": { "name": "Bridges", "collapsed": true },
+ "1444856087950.3635": { "name": "Marine labels", "collapsed": true },
+ "1456969573402.7817": { "name": "Hillshading", "collapsed": true },
+ "1444862510685.128": { "name": "City labels", "collapsed": true },
+ "1444855769305.6016": { "name": "Tunnels", "collapsed": true },
+ "1456970288113.8113": { "name": "Landcover", "collapsed": true },
+ "1444856144497.7825": { "name": "Country labels", "collapsed": true },
+ "1444933456003.5437": { "name": "POI labels (scalerank 4)", "collapsed": true }
+ }
+ },
+ "sources": {
+ "composite": { "url": "mapbox://mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v7", "type": "vector" }
+ },
+ "sprite": "mapbox://sprites/mapbox/streets-v10",
+ "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
+ "visibility": "public",
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "layout": {},
+ "paint": {
+ "background-color": { "base": 1, "stops": [ [ 11, "hsl(35, 32%, 91%)" ], [ 13, "hsl(35, 12%, 89%)" ] ] }
+ }
+ },
+ {
+ "id": "landcover_snow",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456970288113.8113" },
+ "source": "composite",
+ "source-layer": "landcover",
+ "filter": [ "==", "class", "snow" ],
+ "layout": {},
+ "paint": { "fill-color": "hsl(0, 0%, 100%)", "fill-opacity": 0.2, "fill-antialias": false }
+ },
+ {
+ "id": "landcover_wood",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456970288113.8113" },
+ "source": "composite",
+ "source-layer": "landcover",
+ "maxzoom": 14,
+ "filter": [ "==", "class", "wood" ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(75, 62%, 81%)",
+ "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "landcover_scrub",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456970288113.8113" },
+ "source": "composite",
+ "source-layer": "landcover",
+ "maxzoom": 14,
+ "filter": [ "==", "class", "scrub" ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(75, 62%, 81%)",
+ "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "landcover_grass",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456970288113.8113" },
+ "source": "composite",
+ "source-layer": "landcover",
+ "maxzoom": 14,
+ "filter": [ "==", "class", "grass" ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(75, 62%, 81%)",
+ "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "landcover_crop",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456970288113.8113" },
+ "source": "composite",
+ "source-layer": "landcover",
+ "maxzoom": 14,
+ "filter": [ "==", "class", "crop" ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(75, 62%, 81%)",
+ "fill-opacity": { "base": 1.5, "stops": [ [ 2, 0.3 ], [ 7, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "national_park",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "landuse_overlay",
+ "filter": [ "==", "class", "national_park" ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(100, 58%, 76%)",
+ "fill-opacity": { "base": 1, "stops": [ [ 5, 0 ], [ 6, 0.5 ] ] }
+ }
+ },
+ {
+ "id": "hospital",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "landuse",
+ "filter": [ "==", "class", "hospital" ],
+ "layout": {},
+ "paint": {
+ "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(340, 37%, 87%)" ], [ 16, "hsl(340, 63%, 89%)" ] ] }
+ }
+ },
+ {
+ "id": "school",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "landuse",
+ "filter": [ "==", "class", "school" ],
+ "layout": {},
+ "paint": {
+ "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(50, 47%, 81%)" ], [ 16, "hsl(50, 63%, 84%)" ] ] }
+ }
+ },
+ {
+ "id": "park",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "landuse",
+ "filter": [ "==", "class", "park" ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(100, 58%, 76%)",
+ "fill-opacity": { "base": 1, "stops": [ [ 5, 0 ], [ 6, 1 ] ] }
+ }
+ },
+ {
+ "id": "pitch",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "landuse",
+ "filter": [ "==", "class", "pitch" ],
+ "layout": {},
+ "paint": { "fill-color": "hsl(100, 57%, 72%)" }
+ },
+ {
+ "id": "pitch-line",
+ "type": "line",
+ "source": "composite",
+ "source-layer": "landuse",
+ "minzoom": 15,
+ "filter": [ "==", "class", "pitch" ],
+ "layout": { "line-join": "miter" },
+ "paint": { "line-color": "hsl(75, 57%, 84%)" }
+ },
+ {
+ "id": "cemetery",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "landuse",
+ "filter": [ "==", "class", "cemetery" ],
+ "layout": {},
+ "paint": { "fill-color": "hsl(75, 37%, 81%)" }
+ },
+ {
+ "id": "industrial",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "landuse",
+ "filter": [ "==", "class", "industrial" ],
+ "layout": {},
+ "paint": {
+ "fill-color": { "base": 1, "stops": [ [ 15.5, "hsl(230, 15%, 86%)" ], [ 16, "hsl(230, 29%, 89%)" ] ] }
+ }
+ },
+ {
+ "id": "sand",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "landuse",
+ "filter": [ "==", "class", "sand" ],
+ "layout": {},
+ "paint": { "fill-color": "hsl(60, 46%, 87%)" }
+ },
+ {
+ "id": "hillshade_highlight_bright",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456969573402.7817" },
+ "source": "composite",
+ "source-layer": "hillshade",
+ "maxzoom": 16,
+ "filter": [ "==", "level", 94 ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(0, 0%, 100%)",
+ "fill-opacity": { "stops": [ [ 14, 0.12 ], [ 16, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "hillshade_highlight_med",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456969573402.7817" },
+ "source": "composite",
+ "source-layer": "hillshade",
+ "maxzoom": 16,
+ "filter": [ "==", "level", 90 ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(0, 0%, 100%)",
+ "fill-opacity": { "stops": [ [ 14, 0.12 ], [ 16, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "hillshade_shadow_faint",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456969573402.7817" },
+ "source": "composite",
+ "source-layer": "hillshade",
+ "maxzoom": 16,
+ "filter": [ "==", "level", 89 ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(56, 59%, 22%)",
+ "fill-opacity": { "stops": [ [ 14, 0.05 ], [ 16, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "hillshade_shadow_med",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456969573402.7817" },
+ "source": "composite",
+ "source-layer": "hillshade",
+ "maxzoom": 16,
+ "filter": [ "==", "level", 78 ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(56, 59%, 22%)",
+ "fill-opacity": { "stops": [ [ 14, 0.05 ], [ 16, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "hillshade_shadow_dark",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456969573402.7817" },
+ "source": "composite",
+ "source-layer": "hillshade",
+ "maxzoom": 16,
+ "filter": [ "==", "level", 67 ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(56, 59%, 22%)",
+ "fill-opacity": { "stops": [ [ 14, 0.06 ], [ 16, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "hillshade_shadow_extreme",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1456969573402.7817" },
+ "source": "composite",
+ "source-layer": "hillshade",
+ "maxzoom": 16,
+ "filter": [ "==", "level", 56 ],
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(56, 59%, 22%)",
+ "fill-opacity": { "stops": [ [ 14, 0.06 ], [ 16, 0 ] ] },
+ "fill-antialias": false
+ }
+ },
+ {
+ "id": "waterway-river-canal",
+ "type": "line",
+ "source": "composite",
+ "source-layer": "waterway",
+ "minzoom": 8,
+ "filter": [ "in", "class", "canal", "river" ],
+ "layout": {
+ "line-cap": { "base": 1, "stops": [ [ 0, "butt" ], [ 11, "round" ] ] },
+ "line-join": "round"
+ },
+ "paint": {
+ "line-color": "hsl(205, 87%, 76%)",
+ "line-width": { "base": 1.3, "stops": [ [ 8.5, 0.1 ], [ 20, 8 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 8, 0 ], [ 8.5, 1 ] ] }
+ }
+ },
+ {
+ "id": "waterway-small",
+ "type": "line",
+ "source": "composite",
+ "source-layer": "waterway",
+ "minzoom": 13,
+ "filter": [ "!in", "class", "canal", "river" ],
+ "layout": { "line-join": "round", "line-cap": "round" },
+ "paint": {
+ "line-color": "hsl(205, 87%, 76%)",
+ "line-width": { "base": 1.35, "stops": [ [ 13.5, 0.1 ], [ 20, 3 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 13, 0 ], [ 13.5, 1 ] ] }
+ }
+ },
+ {
+ "id": "water-shadow",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "water",
+ "layout": {},
+ "paint": {
+ "fill-color": "hsl(215, 84%, 69%)",
+ "fill-translate": { "base": 1.2, "stops": [ [ 7, [ 0, 0 ] ], [ 16, [ -1, -1 ] ] ] },
+ "fill-translate-anchor": "viewport",
+ "fill-opacity": 1
+ }
+ },
+ { "id": "water", "ref": "water-shadow", "paint": { "fill-color": "hsl(196, 80%, 70%)" } },
+ {
+ "id": "barrier_line-land-polygon",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "barrier_line",
+ "filter": [ "all", [ "==", "$type", "Polygon" ], [ "==", "class", "land" ] ],
+ "layout": {},
+ "paint": { "fill-color": "hsl(35, 12%, 89%)" }
+ },
+ {
+ "id": "barrier_line-land-line",
+ "type": "line",
+ "source": "composite",
+ "source-layer": "barrier_line",
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "class", "land" ] ],
+ "layout": { "line-cap": "round" },
+ "paint": {
+ "line-width": { "base": 1.99, "stops": [ [ 14, 0.75 ], [ 20, 40 ] ] },
+ "line-color": "hsl(35, 12%, 89%)"
+ }
+ },
+ {
+ "id": "aeroway-polygon",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1444934828655.3389" },
+ "source": "composite",
+ "source-layer": "aeroway",
+ "minzoom": 11,
+ "filter": [ "all", [ "==", "$type", "Polygon" ], [ "!=", "type", "apron" ] ],
+ "layout": {},
+ "paint": {
+ "fill-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] },
+ "fill-opacity": { "base": 1, "stops": [ [ 11, 0 ], [ 11.5, 1 ] ] }
+ }
+ },
+ {
+ "id": "aeroway-runway",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444934828655.3389" },
+ "source": "composite",
+ "source-layer": "aeroway",
+ "minzoom": 9,
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "runway" ] ],
+ "layout": {},
+ "paint": {
+ "line-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] },
+ "line-width": { "base": 1.5, "stops": [ [ 9, 1 ], [ 18, 80 ] ] }
+ }
+ },
+ {
+ "id": "aeroway-taxiway",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444934828655.3389" },
+ "source": "composite",
+ "source-layer": "aeroway",
+ "minzoom": 9,
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "taxiway" ] ],
+ "layout": {},
+ "paint": {
+ "line-color": { "base": 1, "stops": [ [ 15, "hsl(230, 23%, 82%)" ], [ 16, "hsl(230, 37%, 84%)" ] ] },
+ "line-width": { "base": 1.5, "stops": [ [ 10, 0.5 ], [ 18, 20 ] ] }
+ }
+ },
+ {
+ "id": "building-line",
+ "type": "line",
+ "source": "composite",
+ "source-layer": "building",
+ "minzoom": 15,
+ "filter": [ "all", [ "!=", "type", "building:part" ], [ "==", "underground", "false" ] ],
+ "layout": {},
+ "paint": {
+ "line-color": "hsl(35, 6%, 79%)",
+ "line-width": { "base": 1.5, "stops": [ [ 15, 0.75 ], [ 20, 3 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 15.5, 0 ], [ 16, 1 ] ] }
+ }
+ },
+ {
+ "id": "building",
+ "type": "fill",
+ "source": "composite",
+ "source-layer": "building",
+ "minzoom": 15,
+ "filter": [ "all", [ "!=", "type", "building:part" ], [ "==", "underground", "false" ] ],
+ "layout": {},
+ "paint": {
+ "fill-color": { "base": 1, "stops": [ [ 15, "hsl(35, 11%, 88%)" ], [ 16, "hsl(35, 8%, 85%)" ] ] },
+ "fill-opacity": { "base": 1, "stops": [ [ 15.5, 0 ], [ 16, 1 ] ] },
+ "fill-outline-color": "hsl(35, 6%, 79%)"
+ }
+ },
+ {
+ "id": "tunnel-street-low",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "street" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] }
+ }
+ },
+ {
+ "id": "tunnel-street_limited-low",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] }
+ }
+ },
+ {
+ "id": "tunnel-service-link-track-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 14,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "service", "track" ],
+ [ "==", "structure", "tunnel" ],
+ [ "!=", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 19%, 75%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] },
+ "line-dasharray": [ 3, 3 ]
+ }
+ },
+ {
+ "id": "tunnel-street_limited-case",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-street_limited-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 19%, 75%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-dasharray": [ 3, 3 ],
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "tunnel-street-case",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-street-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 19%, 75%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-dasharray": [ 3, 3 ],
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "tunnel-secondary-tertiary-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "in", "class", "secondary", "tertiary" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] },
+ "line-dasharray": [ 3, 3 ],
+ "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] },
+ "line-color": "hsl(230, 19%, 75%)"
+ }
+ },
+ {
+ "id": "tunnel-primary-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "primary" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-dasharray": [ 3, 3 ],
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": "hsl(230, 19%, 75%)"
+ }
+ },
+ {
+ "id": "tunnel-trunk_link-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "trunk_link" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-dasharray": [ 3, 3 ]
+ }
+ },
+ {
+ "id": "tunnel-motorway_link-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "motorway_link" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-dasharray": [ 3, 3 ]
+ }
+ },
+ {
+ "id": "tunnel-trunk-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "trunk" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-opacity": 1,
+ "line-dasharray": [ 3, 3 ]
+ }
+ },
+ {
+ "id": "tunnel-motorway-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "motorway" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-opacity": 1,
+ "line-dasharray": [ 3, 3 ]
+ }
+ },
+ {
+ "id": "tunnel-construction",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 14,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "construction" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-join": "miter" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] },
+ "line-dasharray": {
+ "base": 1,
+ "stops": [
+ [ 14, [ 0.4, 0.8 ] ],
+ [ 15, [ 0.3, 0.6 ] ],
+ [ 16, [ 0.2, 0.3 ] ],
+ [ 17, [ 0.2, 0.25 ] ],
+ [ 18, [ 0.15, 0.15 ] ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "tunnel-path",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "path" ],
+ [ "==", "structure", "tunnel" ],
+ [ "!=", "type", "steps" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] },
+ "line-dasharray": {
+ "base": 1,
+ "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ]
+ },
+ "line-color": "hsl(35, 26%, 95%)",
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "tunnel-steps",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "structure", "tunnel" ], [ "==", "type", "steps" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] },
+ "line-color": "hsl(35, 26%, 95%)",
+ "line-dasharray": {
+ "base": 1,
+ "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ]
+ },
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "tunnel-trunk_link",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-trunk_link-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(46, 77%, 78%)",
+ "line-opacity": 1,
+ "line-dasharray": [ 1, 0 ]
+ }
+ },
+ {
+ "id": "tunnel-motorway_link",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-motorway_link-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(26, 100%, 78%)",
+ "line-opacity": 1,
+ "line-dasharray": [ 1, 0 ]
+ }
+ },
+ {
+ "id": "tunnel-pedestrian",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": 1,
+ "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] }
+ }
+ },
+ {
+ "id": "tunnel-service-link-track",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-service-link-track-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-dasharray": [ 1, 0 ]
+ }
+ },
+ {
+ "id": "tunnel-street_limited",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-street_limited-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(35, 14%, 93%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "tunnel-street",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-street-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "tunnel-secondary-tertiary",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-secondary-tertiary-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": 1,
+ "line-dasharray": [ 1, 0 ],
+ "line-blur": 0
+ }
+ },
+ {
+ "id": "tunnel-primary",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-primary-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": 1,
+ "line-dasharray": [ 1, 0 ],
+ "line-blur": 0
+ }
+ },
+ {
+ "id": "tunnel-oneway-arrows-blue-minor",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "path", "pedestrian", "service", "track" ],
+ [ "==", "oneway", "true" ],
+ [ "==", "structure", "tunnel" ],
+ [ "!=", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] },
+ "symbol-spacing": 200,
+ "icon-padding": 2
+ },
+ "paint": {}
+ },
+ {
+ "id": "tunnel-oneway-arrows-blue-major",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 15,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ],
+ [ "==", "oneway", "true" ],
+ [ "==", "structure", "tunnel" ],
+ [ "!=", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] },
+ "symbol-spacing": 200,
+ "icon-padding": 2
+ },
+ "paint": {}
+ },
+ {
+ "id": "tunnel-trunk",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "trunk" ], [ "==", "structure", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": "hsl(46, 77%, 78%)"
+ }
+ },
+ {
+ "id": "tunnel-motorway",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "ref": "tunnel-motorway-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-dasharray": [ 1, 0 ],
+ "line-opacity": 1,
+ "line-color": "hsl(26, 100%, 78%)",
+ "line-blur": 0
+ }
+ },
+ {
+ "id": "tunnel-oneway-arrows-white",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855769305.6016" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "motorway", "motorway_link", "trunk" ],
+ [ "==", "oneway", "true" ],
+ [ "==", "structure", "tunnel" ],
+ [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] },
+ "symbol-spacing": 200,
+ "icon-padding": 2
+ },
+ "paint": {}
+ },
+ {
+ "id": "ferry",
+ "type": "line",
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "ferry" ] ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-color": { "base": 1, "stops": [ [ 15, "hsl(205, 73%, 63%)" ], [ 17, "hsl(230, 73%, 63%)" ] ] },
+ "line-opacity": 1,
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] },
+ "line-dasharray": { "base": 1, "stops": [ [ 12, [ 1, 0 ] ], [ 13, [ 12, 4 ] ] ] }
+ }
+ },
+ {
+ "id": "ferry_auto",
+ "type": "line",
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "type", "ferry_auto" ] ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-color": { "base": 1, "stops": [ [ 15, "hsl(205, 73%, 63%)" ], [ 17, "hsl(230, 73%, 63%)" ] ] },
+ "line-opacity": 1,
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-path-bg",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "path" ],
+ [ "!in", "structure", "bridge", "tunnel" ],
+ [ "!in", "type", "crossing", "sidewalk", "steps" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] },
+ "line-dasharray": [ 1, 0 ],
+ "line-color": "hsl(230, 17%, 82%)",
+ "line-blur": 0,
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] }
+ }
+ },
+ {
+ "id": "road-steps-bg",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "!in", "structure", "bridge", "tunnel" ], [ "==", "type", "steps" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 17, 4.6 ], [ 18, 7 ] ] },
+ "line-color": "hsl(230, 17%, 82%)",
+ "line-dasharray": [ 1, 0 ],
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] }
+ }
+ },
+ {
+ "id": "road-sidewalk-bg",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "!in", "structure", "bridge", "tunnel" ],
+ [ "in", "type", "crossing", "sidewalk" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] },
+ "line-dasharray": [ 1, 0 ],
+ "line-color": "hsl(230, 17%, 82%)",
+ "line-blur": 0,
+ "line-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 0.75 ] ] }
+ }
+ },
+ {
+ "id": "turning-features-outline",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 15,
+ "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "class", "turning_circle", "turning_loop" ] ],
+ "layout": {
+ "icon-image": "turning-circle-outline",
+ "icon-size": { "base": 1.5, "stops": [ [ 14, 0.122 ], [ 18, 0.969 ], [ 20, 1 ] ] },
+ "icon-allow-overlap": true,
+ "icon-ignore-placement": true,
+ "icon-padding": 0,
+ "icon-rotation-alignment": "map"
+ },
+ "paint": {}
+ },
+ {
+ "id": "road-pedestrian-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 12,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "none" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 14, 2 ], [ 18, 14.5 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": 0,
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-street-low",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "street" ], [ "==", "structure", "none" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "stops": [ [ 11, 0 ], [ 11.25, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] }
+ }
+ },
+ {
+ "id": "road-street_limited-low",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "none" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "stops": [ [ 11, 0 ], [ 11.25, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] }
+ }
+ },
+ {
+ "id": "road-service-link-track-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 14,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "service", "track" ],
+ [ "!in", "structure", "bridge", "tunnel" ],
+ [ "!=", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }
+ }
+ },
+ {
+ "id": "road-street_limited-case",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-street_limited-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-street-case",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-street-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-secondary-tertiary-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "secondary", "tertiary" ],
+ [ "!in", "structure", "bridge", "tunnel" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 9.99, 0 ], [ 10, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-primary-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "primary" ], [ "!in", "structure", "bridge", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 9.99, 0 ], [ 10, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-motorway_link-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 10,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "motorway_link" ], [ "!in", "structure", "bridge", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-trunk_link-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "!in", "structure", "bridge", "tunnel" ], [ "==", "type", "trunk_link" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-trunk-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "trunk" ], [ "!in", "structure", "bridge", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 6, 0 ], [ 6.1, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-motorway-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "motorway" ], [ "!in", "structure", "bridge", "tunnel" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }
+ }
+ },
+ {
+ "id": "road-construction",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 14,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "construction" ], [ "==", "structure", "none" ] ]
+ ],
+ "layout": { "line-join": "miter" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] },
+ "line-dasharray": {
+ "base": 1,
+ "stops": [
+ [ 14, [ 0.4, 0.8 ] ],
+ [ 15, [ 0.3, 0.6 ] ],
+ [ 16, [ 0.2, 0.3 ] ],
+ [ 17, [ 0.2, 0.25 ] ],
+ [ 18, [ 0.15, 0.15 ] ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "road-sidewalks",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-sidewalk-bg",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-dasharray": {
+ "base": 1,
+ "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ]
+ },
+ "line-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-path",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-path-bg",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-dasharray": {
+ "base": 1,
+ "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ]
+ },
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-steps",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-steps-bg",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-dasharray": {
+ "base": 1,
+ "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ]
+ },
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-trunk_link",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-trunk_link-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(46, 85%, 67%)",
+ "line-opacity": 1
+ }
+ },
+ {
+ "id": "road-motorway_link",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-motorway_link-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(26, 100%, 68%)",
+ "line-opacity": 1
+ }
+ },
+ {
+ "id": "road-pedestrian",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-pedestrian-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": 1,
+ "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] }
+ }
+ },
+ {
+ "id": "road-pedestrian-polygon-fill",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 12,
+ "filter": [
+ "all",
+ [ "==", "$type", "Polygon" ],
+ [ "all", [ "in", "class", "path", "pedestrian" ], [ "==", "structure", "none" ] ]
+ ],
+ "layout": {},
+ "paint": {
+ "fill-color": { "base": 1, "stops": [ [ 16, "hsl(230, 16%, 94%)" ], [ 16.25, "hsl(230, 50%, 98%)" ] ] },
+ "fill-outline-color": "hsl(230, 26%, 88%)",
+ "fill-opacity": 1
+ }
+ },
+ {
+ "id": "road-pedestrian-polygon-pattern",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-pedestrian-polygon-fill",
+ "paint": {
+ "fill-color": "hsl(0, 0%, 100%)",
+ "fill-outline-color": "hsl(35, 10%, 83%)",
+ "fill-pattern": "pedestrian-polygon",
+ "fill-opacity": { "base": 1, "stops": [ [ 16, 0 ], [ 16.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-polygon",
+ "type": "fill",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 12,
+ "filter": [
+ "all",
+ [ "==", "$type", "Polygon" ],
+ [
+ "all",
+ [ "!in", "class", "motorway", "path", "pedestrian", "trunk" ],
+ [ "!in", "structure", "bridge", "tunnel" ]
+ ]
+ ],
+ "layout": {},
+ "paint": { "fill-color": "hsl(0, 0%, 100%)", "fill-outline-color": "#d6d9e6" }
+ },
+ {
+ "id": "road-service-link-track",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-service-link-track-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] },
+ "line-color": "hsl(0, 0%, 100%)"
+ }
+ },
+ {
+ "id": "road-street_limited",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-street_limited-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(35, 14%, 93%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-street",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-street-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-secondary-tertiary",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-secondary-tertiary-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] },
+ "line-color": { "base": 1, "stops": [ [ 5, "hsl(35, 32%, 91%)" ], [ 8, "hsl(0, 0%, 100%)" ] ] },
+ "line-opacity": { "base": 1.2, "stops": [ [ 5, 0 ], [ 5.5, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-primary",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-primary-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": { "base": 1, "stops": [ [ 5, "hsl(35, 32%, 91%)" ], [ 7, "hsl(0, 0%, 100%)" ] ] },
+ "line-opacity": 1
+ }
+ },
+ {
+ "id": "road-oneway-arrows-blue-minor",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "path", "pedestrian", "service", "track" ],
+ [ "==", "oneway", "true" ],
+ [ "!in", "structure", "bridge", "tunnel" ],
+ [ "!=", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] },
+ "icon-rotation-alignment": "map",
+ "icon-padding": 2,
+ "symbol-spacing": 200
+ },
+ "paint": {}
+ },
+ {
+ "id": "road-oneway-arrows-blue-major",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 15,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ],
+ [ "==", "oneway", "true" ],
+ [ "!in", "structure", "bridge", "tunnel" ],
+ [ "!=", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] },
+ "icon-rotation-alignment": "map",
+ "icon-padding": 2,
+ "symbol-spacing": 200
+ },
+ "paint": {}
+ },
+ {
+ "id": "road-trunk",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-trunk-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": {
+ "base": 1,
+ "stops": [ [ 6, "hsl(0, 0%, 100%)" ], [ 6.1, "hsl(46, 80%, 60%)" ], [ 9, "hsl(46, 85%, 67%)" ] ]
+ }
+ }
+ },
+ {
+ "id": "road-motorway",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-motorway-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": { "base": 1, "stops": [ [ 8, "hsl(26, 87%, 62%)" ], [ 9, "hsl(26, 100%, 68%)" ] ] }
+ }
+ },
+ {
+ "id": "road-rail",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "major_rail", "minor_rail" ],
+ [ "!in", "structure", "bridge", "tunnel" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] },
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] }
+ }
+ },
+ {
+ "id": "road-rail-tracks",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "ref": "road-rail",
+ "paint": {
+ "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] },
+ "line-width": { "base": 1.5, "stops": [ [ 14, 4 ], [ 20, 8 ] ] },
+ "line-dasharray": [ 0.1, 15 ],
+ "line-opacity": { "base": 1, "stops": [ [ 13.75, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "level-crossings",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 16,
+ "filter": [ "all", [ "==", "$type", "Point" ], [ "==", "class", "level_crossing" ] ],
+ "layout": { "icon-size": 1, "icon-image": "level-crossing", "icon-allow-overlap": true },
+ "paint": {}
+ },
+ {
+ "id": "road-oneway-arrows-white",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "motorway", "motorway_link", "trunk" ],
+ [ "==", "oneway", "true" ],
+ [ "!in", "structure", "bridge", "tunnel" ],
+ [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] },
+ "icon-padding": 2,
+ "symbol-spacing": 200
+ },
+ "paint": {}
+ },
+ {
+ "id": "turning-features",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855786460.0557" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 15,
+ "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "class", "turning_circle", "turning_loop" ] ],
+ "layout": {
+ "icon-image": "turning-circle",
+ "icon-size": { "base": 1.5, "stops": [ [ 14, 0.095 ], [ 18, 1 ] ] },
+ "icon-allow-overlap": true,
+ "icon-ignore-placement": true,
+ "icon-padding": 0,
+ "icon-rotation-alignment": "map"
+ },
+ "paint": {}
+ },
+ {
+ "id": "bridge-path-bg",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "path" ],
+ [ "==", "structure", "bridge" ],
+ [ "!=", "type", "steps" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 18, 7 ] ] },
+ "line-dasharray": [ 1, 0 ],
+ "line-color": "hsl(230, 17%, 82%)",
+ "line-blur": 0,
+ "line-opacity": { "base": 1, "stops": [ [ 15, 0 ], [ 15.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-steps-bg",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "structure", "bridge" ], [ "==", "type", "steps" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 2 ], [ 17, 4.6 ], [ 18, 7 ] ] },
+ "line-color": "hsl(230, 17%, 82%)",
+ "line-dasharray": [ 1, 0 ],
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 0.75 ] ] }
+ }
+ },
+ {
+ "id": "bridge-pedestrian-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "pedestrian" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 14, 2 ], [ 18, 14.5 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": 0,
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-street-low",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "street" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] }
+ }
+ },
+ {
+ "id": "bridge-street_limited-low",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "stops": [ [ 11.5, 0 ], [ 12, 1 ], [ 14, 1 ], [ 14.01, 0 ] ] }
+ }
+ },
+ {
+ "id": "bridge-service-link-track-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 14,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "service", "track" ],
+ [ "==", "structure", "bridge" ],
+ [ "!=", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] }
+ }
+ },
+ {
+ "id": "bridge-street_limited-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "street_limited" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }
+ }
+ },
+ {
+ "id": "bridge-street-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "street" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] },
+ "line-gap-width": { "base": 1.5, "stops": [ [ 13, 0 ], [ 14, 2 ], [ 18, 18 ] ] }
+ }
+ },
+ {
+ "id": "bridge-secondary-tertiary-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "in", "class", "secondary", "tertiary" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.2, "stops": [ [ 10, 0.75 ], [ 18, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] },
+ "line-translate": [ 0, 0 ]
+ }
+ },
+ {
+ "id": "bridge-primary-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "primary" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-translate": [ 0, 0 ]
+ }
+ },
+ {
+ "id": "bridge-trunk_link-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "!in", "layer", 2, 3, 4, 5 ],
+ [ "==", "structure", "bridge" ],
+ [ "==", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-motorway_link-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "motorway_link" ],
+ [ "!in", "layer", 2, 3, 4, 5 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-opacity": 1
+ }
+ },
+ {
+ "id": "bridge-trunk-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "trunk" ],
+ [ "!in", "layer", 2, 3, 4, 5 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }
+ }
+ },
+ {
+ "id": "bridge-motorway-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "motorway" ],
+ [ "!in", "layer", 2, 3, 4, 5 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }
+ }
+ },
+ {
+ "id": "bridge-construction",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 14,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "class", "construction" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-join": "miter" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(230, 24%, 87%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] },
+ "line-dasharray": {
+ "base": 1,
+ "stops": [
+ [ 14, [ 0.4, 0.8 ] ],
+ [ 15, [ 0.3, 0.6 ] ],
+ [ 16, [ 0.2, 0.3 ] ],
+ [ 17, [ 0.2, 0.25 ] ],
+ [ 18, [ 0.15, 0.15 ] ]
+ ]
+ }
+ }
+ },
+ {
+ "id": "bridge-path",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "path" ],
+ [ "==", "structure", "bridge" ],
+ [ "!=", "type", "steps" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 18, 4 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-dasharray": {
+ "base": 1,
+ "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 1, 0.5 ] ] ]
+ },
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-steps",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "ref": "bridge-steps-bg",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 15, 1 ], [ 16, 1.6 ], [ 18, 6 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-dasharray": {
+ "base": 1,
+ "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.75, 1 ] ], [ 16, [ 1, 0.75 ] ], [ 17, [ 0.3, 0.3 ] ] ]
+ },
+ "line-opacity": { "base": 1, "stops": [ [ 14, 0 ], [ 14.25, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-trunk_link",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "!in", "layer", 2, 3, 4, 5 ],
+ [ "==", "structure", "bridge" ],
+ [ "==", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(46, 85%, 67%)"
+ }
+ },
+ {
+ "id": "bridge-motorway_link",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "motorway_link" ],
+ [ "!in", "layer", 2, 3, 4, 5 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(26, 100%, 68%)"
+ }
+ },
+ {
+ "id": "bridge-pedestrian",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "ref": "bridge-pedestrian-case",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": 1,
+ "line-dasharray": { "base": 1, "stops": [ [ 14, [ 1, 0 ] ], [ 15, [ 1.5, 0.4 ] ], [ 16, [ 1, 0.2 ] ] ] }
+ }
+ },
+ {
+ "id": "bridge-service-link-track",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 14,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "service", "track" ],
+ [ "==", "structure", "bridge" ],
+ [ "!=", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 18, 12 ] ] },
+ "line-color": "hsl(0, 0%, 100%)"
+ }
+ },
+ {
+ "id": "bridge-street_limited",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "ref": "bridge-street_limited-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(35, 14%, 93%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-street",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "ref": "bridge-street-low",
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12.5, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-secondary-tertiary",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "structure", "bridge" ], [ "in", "type", "secondary", "tertiary" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 8.5, 0.5 ], [ 10, 0.75 ], [ 18, 26 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": { "base": 1.2, "stops": [ [ 5, 0 ], [ 5.5, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-primary",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "==", "structure", "bridge" ], [ "==", "type", "primary" ] ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-opacity": 1
+ }
+ },
+ {
+ "id": "bridge-oneway-arrows-blue-minor",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "path", "pedestrian", "service", "track" ],
+ [ "==", "oneway", "true" ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 17, "oneway-small" ], [ 18, "oneway-large" ] ] },
+ "symbol-spacing": 200,
+ "icon-rotation-alignment": "map",
+ "icon-padding": 2
+ },
+ "paint": {}
+ },
+ {
+ "id": "bridge-oneway-arrows-blue-major",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 15,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "primary", "secondary", "street", "street_limited", "tertiary" ],
+ [ "==", "oneway", "true" ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 16, "oneway-small" ], [ 17, "oneway-large" ] ] },
+ "symbol-spacing": 200,
+ "icon-rotation-alignment": "map",
+ "icon-padding": 2
+ },
+ "paint": {}
+ },
+ {
+ "id": "bridge-trunk",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "trunk" ],
+ [ "!in", "layer", 2, 3, 4, 5 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": "hsl(46, 85%, 67%)"
+ }
+ },
+ {
+ "id": "bridge-motorway",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "motorway" ],
+ [ "!in", "layer", 2, 3, 4, 5 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": "hsl(26, 100%, 68%)"
+ }
+ },
+ {
+ "id": "bridge-rail",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "all", [ "in", "class", "major_rail", "minor_rail" ], [ "==", "structure", "bridge" ] ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] },
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-rail-tracks",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "ref": "bridge-rail",
+ "paint": {
+ "line-color": { "stops": [ [ 13, "hsl(50, 17%, 82%)" ], [ 16, "hsl(230, 10%, 74%)" ] ] },
+ "line-width": { "base": 1.5, "stops": [ [ 14, 4 ], [ 20, 8 ] ] },
+ "line-dasharray": [ 0.1, 15 ],
+ "line-opacity": { "base": 1, "stops": [ [ 13.75, 0 ], [ 20, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-trunk_link-2-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ ">=", "layer", 2 ],
+ [ "==", "structure", "bridge" ],
+ [ "==", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 10.99, 0 ], [ 11, 1 ] ] }
+ }
+ },
+ {
+ "id": "bridge-motorway_link-2-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "motorway_link" ],
+ [ ">=", "layer", 2 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.75 ], [ 20, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-opacity": 1
+ }
+ },
+ {
+ "id": "bridge-trunk-2-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "trunk" ],
+ [ ">=", "layer", 2 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }
+ }
+ },
+ {
+ "id": "bridge-motorway-2-case",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "motorway" ],
+ [ ">=", "layer", 2 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 10, 1 ], [ 16, 2 ] ] },
+ "line-color": "hsl(0, 0%, 100%)",
+ "line-gap-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] }
+ }
+ },
+ {
+ "id": "bridge-trunk_link-2",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ ">=", "layer", 2 ],
+ [ "==", "structure", "bridge" ],
+ [ "==", "type", "trunk_link" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(46, 85%, 67%)"
+ }
+ },
+ {
+ "id": "bridge-motorway_link-2",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "motorway_link" ],
+ [ ">=", "layer", 2 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 12, 0.5 ], [ 14, 2 ], [ 18, 18 ] ] },
+ "line-color": "hsl(26, 100%, 68%)"
+ }
+ },
+ {
+ "id": "bridge-trunk-2",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "trunk" ],
+ [ ">=", "layer", 2 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": "hsl(46, 85%, 67%)"
+ }
+ },
+ {
+ "id": "bridge-motorway-2",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "==", "class", "motorway" ],
+ [ ">=", "layer", 2 ],
+ [ "==", "structure", "bridge" ]
+ ]
+ ],
+ "layout": { "line-cap": "round", "line-join": "round" },
+ "paint": {
+ "line-width": { "base": 1.5, "stops": [ [ 5, 0.75 ], [ 18, 32 ] ] },
+ "line-color": "hsl(26, 100%, 68%)"
+ }
+ },
+ {
+ "id": "bridge-oneway-arrows-white",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444855799204.86" },
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 16,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "all",
+ [ "in", "class", "link", "motorway", "motorway_link", "trunk" ],
+ [ "==", "oneway", "true" ],
+ [ "==", "structure", "bridge" ],
+ [ "!in", "type", "primary_link", "secondary_link", "tertiary_link" ]
+ ]
+ ],
+ "layout": {
+ "symbol-placement": "line",
+ "icon-image": { "base": 1, "stops": [ [ 16, "oneway-white-small" ], [ 17, "oneway-white-large" ] ] },
+ "symbol-spacing": 200,
+ "icon-padding": 2
+ },
+ "paint": {}
+ },
+ {
+ "id": "aerialway",
+ "type": "line",
+ "source": "composite",
+ "source-layer": "road",
+ "minzoom": 13,
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "class", "aerialway" ] ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-color": "hsl(230, 10%, 74%)",
+ "line-width": { "base": 1.5, "stops": [ [ 14, 0.5 ], [ 20, 1 ] ] }
+ }
+ },
+ {
+ "id": "admin-3-4-boundaries-bg",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444934295202.7542" },
+ "source": "composite",
+ "source-layer": "admin",
+ "filter": [ "all", [ ">=", "admin_level", 3 ], [ "==", "maritime", 0 ] ],
+ "layout": { "line-join": "bevel" },
+ "paint": {
+ "line-color": { "base": 1, "stops": [ [ 8, "hsl(35, 12%, 89%)" ], [ 16, "hsl(230, 49%, 90%)" ] ] },
+ "line-width": { "base": 1, "stops": [ [ 7, 3.75 ], [ 12, 5.5 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 7, 0 ], [ 8, 0.75 ] ] },
+ "line-dasharray": [ 1, 0 ],
+ "line-translate": [ 0, 0 ],
+ "line-blur": { "base": 1, "stops": [ [ 3, 0 ], [ 8, 3 ] ] }
+ }
+ },
+ {
+ "id": "admin-2-boundaries-bg",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444934295202.7542" },
+ "source": "composite",
+ "source-layer": "admin",
+ "minzoom": 1,
+ "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "maritime", 0 ] ],
+ "layout": { "line-join": "miter" },
+ "paint": {
+ "line-width": { "base": 1, "stops": [ [ 3, 3.5 ], [ 10, 8 ] ] },
+ "line-color": { "base": 1, "stops": [ [ 6, "hsl(35, 12%, 89%)" ], [ 8, "hsl(230, 49%, 90%)" ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 3, 0 ], [ 4, 0.5 ] ] },
+ "line-translate": [ 0, 0 ],
+ "line-blur": { "base": 1, "stops": [ [ 3, 0 ], [ 10, 2 ] ] }
+ }
+ },
+ {
+ "id": "admin-3-4-boundaries",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444934295202.7542" },
+ "source": "composite",
+ "source-layer": "admin",
+ "filter": [ "all", [ ">=", "admin_level", 3 ], [ "==", "maritime", 0 ] ],
+ "layout": { "line-join": "round", "line-cap": "round" },
+ "paint": {
+ "line-dasharray": { "base": 1, "stops": [ [ 6, [ 2, 0 ] ], [ 7, [ 2, 2, 6, 2 ] ] ] },
+ "line-width": { "base": 1, "stops": [ [ 7, 0.75 ], [ 12, 1.5 ] ] },
+ "line-opacity": { "base": 1, "stops": [ [ 2, 0 ], [ 3, 1 ] ] },
+ "line-color": { "base": 1, "stops": [ [ 3, "hsl(230, 14%, 77%)" ], [ 7, "hsl(230, 8%, 62%)" ] ] }
+ }
+ },
+ {
+ "id": "admin-2-boundaries",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444934295202.7542" },
+ "source": "composite",
+ "source-layer": "admin",
+ "minzoom": 1,
+ "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "disputed", 0 ], [ "==", "maritime", 0 ] ],
+ "layout": { "line-join": "round", "line-cap": "round" },
+ "paint": {
+ "line-color": "hsl(230, 8%, 51%)",
+ "line-width": { "base": 1, "stops": [ [ 3, 0.5 ], [ 10, 2 ] ] }
+ }
+ },
+ {
+ "id": "admin-2-boundaries-dispute",
+ "type": "line",
+ "metadata": { "mapbox:group": "1444934295202.7542" },
+ "source": "composite",
+ "source-layer": "admin",
+ "minzoom": 1,
+ "filter": [ "all", [ "==", "admin_level", 2 ], [ "==", "disputed", 1 ], [ "==", "maritime", 0 ] ],
+ "layout": { "line-join": "round" },
+ "paint": {
+ "line-dasharray": [ 1.5, 1.5 ],
+ "line-color": "hsl(230, 8%, 51%)",
+ "line-width": { "base": 1, "stops": [ [ 3, 0.5 ], [ 10, 2 ] ] }
+ }
+ },
+ {
+ "id": "housenum-label",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "housenum_label",
+ "minzoom": 17,
+ "layout": {
+ "text-field": "{house_num}",
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "text-padding": 4,
+ "text-max-width": 7,
+ "text-size": 9.5
+ },
+ "paint": {
+ "text-color": "hsl(35, 2%, 69%)",
+ "text-halo-color": "hsl(35, 8%, 85%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0
+ }
+ },
+ {
+ "id": "waterway-label",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "waterway_label",
+ "minzoom": 12,
+ "filter": [ "in", "class", "canal", "river" ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "symbol-placement": "line",
+ "text-max-angle": 30,
+ "text-size": { "base": 1, "stops": [ [ 13, 12 ], [ 18, 16 ] ] }
+ },
+ "paint": {
+ "text-halo-width": 0.5,
+ "text-halo-color": "hsl(196, 80%, 70%)",
+ "text-color": "hsl(230, 48%, 44%)",
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "poi-scalerank4-l15",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933456003.5437" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "minzoom": 17,
+ "filter": [
+ "all",
+ [ ">=", "localrank", 15 ],
+ [
+ "!in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "==", "scalerank", 4 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] },
+ "icon-image": "{maki}-11",
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(26, 25%, 32%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "poi-scalerank4-l1",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933456003.5437" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "minzoom": 15,
+ "filter": [
+ "all",
+ [ "<=", "localrank", 14 ],
+ [
+ "!in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "==", "scalerank", 4 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] },
+ "icon-image": "{maki}-11",
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 1,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(26, 25%, 32%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "poi-parks_scalerank4",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933456003.5437" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "minzoom": 15,
+ "filter": [
+ "all",
+ [
+ "in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "==", "scalerank", 4 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] },
+ "icon-image": "{maki}-11",
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 1,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(100, 100%, 20%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "poi-scalerank3",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933372896.5967" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "filter": [
+ "all",
+ [
+ "!in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "==", "scalerank", 3 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] },
+ "icon-image": "{maki}-11",
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 1,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(26, 25%, 32%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "poi-parks-scalerank3",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933372896.5967" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "filter": [
+ "all",
+ [
+ "in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "==", "scalerank", 3 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] },
+ "icon-image": "{maki}-11",
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(100, 100%, 20%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "road-label-small",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933721429.3076" },
+ "source": "composite",
+ "source-layer": "road_label",
+ "minzoom": 15,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [
+ "!in",
+ "class",
+ "link",
+ "motorway",
+ "pedestrian",
+ "primary",
+ "secondary",
+ "street",
+ "street_limited",
+ "tertiary",
+ "trunk"
+ ]
+ ],
+ "layout": {
+ "text-size": { "base": 1, "stops": [ [ 15, 10 ], [ 20, 13 ] ] },
+ "text-max-angle": 30,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "symbol-placement": "line",
+ "text-padding": 1,
+ "text-rotation-alignment": "map",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1.25,
+ "text-halo-blur": 1
+ }
+ },
+ {
+ "id": "road-label-medium",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933721429.3076" },
+ "source": "composite",
+ "source-layer": "road_label",
+ "minzoom": 11,
+ "filter": [
+ "all",
+ [ "==", "$type", "LineString" ],
+ [ "in", "class", "link", "pedestrian", "street", "street_limited" ]
+ ],
+ "layout": {
+ "text-size": { "base": 1, "stops": [ [ 11, 10 ], [ 20, 14 ] ] },
+ "text-max-angle": 30,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "symbol-placement": "line",
+ "text-padding": 1,
+ "text-rotation-alignment": "map",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "road-label-large",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933721429.3076" },
+ "source": "composite",
+ "source-layer": "road_label",
+ "filter": [ "in", "class", "motorway", "primary", "secondary", "tertiary", "trunk" ],
+ "layout": {
+ "text-size": { "base": 1, "stops": [ [ 9, 10 ], [ 20, 16 ] ] },
+ "text-max-angle": 30,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "symbol-placement": "line",
+ "text-padding": 1,
+ "text-rotation-alignment": "map",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsla(0, 0%, 100%, 0.75)",
+ "text-halo-width": 1,
+ "text-halo-blur": 1
+ }
+ },
+ {
+ "id": "road-shields-black",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933575858.6992" },
+ "source": "composite",
+ "source-layer": "road_label",
+ "filter": [
+ "all",
+ [ "<=", "reflen", 6 ],
+ [
+ "!in",
+ "shield",
+ "at-expressway",
+ "at-motorway",
+ "at-state-b",
+ "bg-motorway",
+ "bg-national",
+ "ch-main",
+ "ch-motorway",
+ "cz-motorway",
+ "cz-road",
+ "de-motorway",
+ "e-road",
+ "fi-main",
+ "gr-motorway",
+ "gr-national",
+ "hr-motorway",
+ "hr-state",
+ "hu-main",
+ "hu-motorway",
+ "nz-state",
+ "pl-expressway",
+ "pl-motorway",
+ "pl-national",
+ "ro-county",
+ "ro-motorway",
+ "ro-national",
+ "rs-motorway",
+ "rs-state-1b",
+ "se-main",
+ "si-expressway",
+ "si-motorway",
+ "sk-highway",
+ "sk-road",
+ "us-interstate",
+ "us-interstate-business",
+ "us-interstate-duplex",
+ "us-interstate-truck",
+ "za-metropolitan",
+ "za-national",
+ "za-provincial",
+ "za-regional"
+ ]
+ ],
+ "layout": {
+ "text-size": 9,
+ "icon-image": "{shield}-{reflen}",
+ "icon-rotation-alignment": "viewport",
+ "text-max-angle": 38,
+ "symbol-spacing": { "base": 1, "stops": [ [ 11, 150 ], [ 14, 200 ] ] },
+ "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ],
+ "symbol-placement": { "base": 1, "stops": [ [ 10, "point" ], [ 11, "line" ] ] },
+ "text-padding": 2,
+ "text-rotation-alignment": "viewport",
+ "text-field": "{ref}",
+ "text-letter-spacing": 0.05,
+ "icon-padding": 2
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 7%)",
+ "icon-halo-color": "rgba(0, 0, 0, 1)",
+ "icon-halo-width": 1,
+ "text-opacity": 1,
+ "icon-color": "white",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0
+ }
+ },
+ {
+ "id": "road-shields-white",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933575858.6992" },
+ "source": "composite",
+ "source-layer": "road_label",
+ "filter": [
+ "all",
+ [ "<=", "reflen", 6 ],
+ [
+ "in",
+ "shield",
+ "at-expressway",
+ "at-motorway",
+ "at-state-b",
+ "bg-motorway",
+ "bg-national",
+ "ch-main",
+ "ch-motorway",
+ "cz-motorway",
+ "cz-road",
+ "de-motorway",
+ "e-road",
+ "fi-main",
+ "gr-motorway",
+ "gr-national",
+ "hr-motorway",
+ "hr-state",
+ "hu-main",
+ "hu-motorway",
+ "nz-state",
+ "pl-expressway",
+ "pl-motorway",
+ "pl-national",
+ "ro-county",
+ "ro-motorway",
+ "ro-national",
+ "rs-motorway",
+ "rs-state-1b",
+ "se-main",
+ "si-expressway",
+ "si-motorway",
+ "sk-highway",
+ "sk-road",
+ "us-interstate",
+ "us-interstate-business",
+ "us-interstate-duplex",
+ "us-interstate-truck",
+ "za-metropolitan",
+ "za-national",
+ "za-provincial",
+ "za-regional"
+ ]
+ ],
+ "layout": {
+ "text-size": 9,
+ "icon-image": "{shield}-{reflen}",
+ "icon-rotation-alignment": "viewport",
+ "text-max-angle": 38,
+ "symbol-spacing": { "base": 1, "stops": [ [ 11, 150 ], [ 14, 200 ] ] },
+ "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ],
+ "symbol-placement": { "base": 1, "stops": [ [ 10, "point" ], [ 11, "line" ] ] },
+ "text-padding": 2,
+ "text-rotation-alignment": "viewport",
+ "text-field": "{ref}",
+ "text-letter-spacing": 0.05,
+ "icon-padding": 2
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 100%)",
+ "icon-halo-color": "rgba(0, 0, 0, 1)",
+ "icon-halo-width": 1,
+ "text-opacity": 1,
+ "icon-color": "white",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0
+ }
+ },
+ {
+ "id": "motorway-junction",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933575858.6992" },
+ "source": "composite",
+ "source-layer": "motorway_junction",
+ "minzoom": 14,
+ "filter": [ "all", [ ">", "reflen", 0 ], [ "<=", "reflen", 9 ] ],
+ "layout": {
+ "text-field": "{ref}",
+ "text-size": 9,
+ "icon-image": "motorway-exit-{reflen}",
+ "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ]
+ },
+ "paint": { "text-color": "hsl(0, 0%, 100%)", "text-translate": [ 0, 0 ] }
+ },
+ {
+ "id": "poi-scalerank2",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933358918.2366" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "filter": [
+ "all",
+ [
+ "!in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "==", "scalerank", 2 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 14, 11 ], [ 20, 14 ] ] },
+ "icon-image": { "stops": [ [ 14, "{maki}-11" ], [ 15, "{maki}-15" ] ] },
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(26, 25%, 32%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "poi-parks-scalerank2",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933358918.2366" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "filter": [
+ "all",
+ [
+ "in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "==", "scalerank", 2 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 14, 11 ], [ 20, 14 ] ] },
+ "icon-image": { "stops": [ [ 14, "{maki}-11" ], [ 15, "{maki}-15" ] ] },
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(100, 100%, 20%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "rail-label",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "rail_station_label",
+ "minzoom": 12,
+ "filter": [ "!=", "maki", "entrance" ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 16, 11 ], [ 20, 13 ] ] },
+ "icon-image": "{network}",
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-offset": [ 0, 0.85 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": { "base": 1, "stops": [ [ 0, "" ], [ 13, "{name_en}" ] ] },
+ "text-letter-spacing": 0.01,
+ "icon-padding": 0,
+ "text-max-width": 7
+ },
+ "paint": {
+ "text-color": "hsl(230, 48%, 44%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "icon-halo-width": 4,
+ "icon-halo-color": "#fff",
+ "text-opacity": { "base": 1, "stops": [ [ 13.99, 0 ], [ 14, 1 ] ] },
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "water-label-sm",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933808272.805" },
+ "source": "composite",
+ "source-layer": "water_label",
+ "minzoom": 15,
+ "filter": [ "<=", "area", 10000 ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "text-max-width": 7,
+ "text-size": { "base": 1, "stops": [ [ 16, 13 ], [ 20, 16 ] ] }
+ },
+ "paint": { "text-color": "hsl(230, 48%, 44%)" }
+ },
+ {
+ "id": "water-label",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933808272.805" },
+ "source": "composite",
+ "source-layer": "water_label",
+ "minzoom": 5,
+ "filter": [ ">", "area", 10000 ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "text-max-width": 7,
+ "text-size": { "base": 1, "stops": [ [ 13, 13 ], [ 18, 18 ] ] }
+ },
+ "paint": { "text-color": "hsl(230, 48%, 44%)" }
+ },
+ {
+ "id": "place-residential",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "place_label",
+ "maxzoom": 18,
+ "filter": [
+ "all",
+ [ "in", "$type", "LineString", "Point", "Polygon" ],
+ [ "all", [ "<=", "localrank", 10 ], [ "==", "type", "residential" ] ]
+ ],
+ "layout": {
+ "text-line-height": 1.2,
+ "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] },
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0 ],
+ "text-rotation-alignment": "viewport",
+ "text-field": "{name_en}",
+ "text-max-width": 7
+ },
+ "paint": {
+ "text-color": "hsl(26, 25%, 32%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "poi-parks-scalerank1",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933322393.2852" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "filter": [
+ "all",
+ [
+ "in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "<=", "scalerank", 1 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] },
+ "icon-image": { "stops": [ [ 13, "{maki}-11" ], [ 14, "{maki}-15" ] ] },
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(100, 100%, 20%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "poi-scalerank1",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444933322393.2852" },
+ "source": "composite",
+ "source-layer": "poi_label",
+ "filter": [
+ "all",
+ [
+ "!in",
+ "maki",
+ "campsite",
+ "cemetery",
+ "dog-park",
+ "garden",
+ "golf",
+ "park",
+ "picnic-site",
+ "playground",
+ "zoo"
+ ],
+ [ "<=", "scalerank", 1 ]
+ ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 14 ] ] },
+ "icon-image": { "stops": [ [ 13, "{maki}-11" ], [ 14, "{maki}-15" ] ] },
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0.65 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(26, 25%, 32%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "airport-label",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "airport_label",
+ "minzoom": 9,
+ "filter": [ "<=", "scalerank", 2 ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 10, 12 ], [ 18, 18 ] ] },
+ "icon-image": { "stops": [ [ 12, "{maki}-11" ], [ 13, "{maki}-15" ] ] },
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0.75 ],
+ "text-rotation-alignment": "viewport",
+ "text-anchor": "top",
+ "text-field": { "stops": [ [ 11, "{ref}" ], [ 12, "{name_en}" ] ] },
+ "text-letter-spacing": 0.01,
+ "text-max-width": 9
+ },
+ "paint": {
+ "text-color": "hsl(230, 48%, 44%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 0.5,
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "place-islet-archipelago-aboriginal",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "place_label",
+ "maxzoom": 16,
+ "filter": [ "in", "type", "aboriginal_lands", "archipelago", "islet" ],
+ "layout": {
+ "text-line-height": 1.2,
+ "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 16 ] ] },
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0 ],
+ "text-rotation-alignment": "viewport",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 8
+ },
+ "paint": {
+ "text-color": "hsl(230, 29%, 35%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "place-neighbourhood",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "place_label",
+ "minzoom": 10,
+ "maxzoom": 16,
+ "filter": [ "==", "type", "neighbourhood" ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-transform": "uppercase",
+ "text-letter-spacing": 0.1,
+ "text-max-width": 7,
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "text-padding": 3,
+ "text-size": { "base": 1, "stops": [ [ 12, 11 ], [ 16, 16 ] ] }
+ },
+ "paint": {
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1,
+ "text-color": "hsl(230, 29%, 35%)",
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "place-suburb",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "place_label",
+ "minzoom": 10,
+ "maxzoom": 16,
+ "filter": [ "==", "type", "suburb" ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-transform": "uppercase",
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "text-letter-spacing": 0.15,
+ "text-max-width": 7,
+ "text-padding": 3,
+ "text-size": { "base": 1, "stops": [ [ 11, 11 ], [ 15, 18 ] ] }
+ },
+ "paint": {
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1,
+ "text-color": "hsl(230, 29%, 35%)",
+ "text-halo-blur": 0.5
+ }
+ },
+ {
+ "id": "place-hamlet",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "place_label",
+ "minzoom": 10,
+ "maxzoom": 16,
+ "filter": [ "==", "type", "hamlet" ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "text-size": { "base": 1, "stops": [ [ 12, 11.5 ], [ 15, 16 ] ] }
+ },
+ "paint": {
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1.25,
+ "text-color": "hsl(0, 0%, 0%)"
+ }
+ },
+ {
+ "id": "place-village",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "place_label",
+ "minzoom": 8,
+ "maxzoom": 15,
+ "filter": [ "==", "type", "village" ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "text-max-width": 7,
+ "text-size": { "base": 1, "stops": [ [ 10, 11.5 ], [ 16, 18 ] ] }
+ },
+ "paint": {
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1.25,
+ "text-color": "hsl(0, 0%, 0%)"
+ }
+ },
+ {
+ "id": "place-town",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "place_label",
+ "minzoom": 6,
+ "maxzoom": 15,
+ "filter": [ "==", "type", "town" ],
+ "layout": {
+ "icon-image": "dot-9",
+ "text-font": {
+ "base": 1,
+ "stops": [
+ [ 11, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ],
+ [ 12, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ]
+ ]
+ },
+ "text-offset": { "base": 1, "stops": [ [ 7, [ 0, -0.15 ] ], [ 8, [ 0, 0 ] ] ] },
+ "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] },
+ "text-field": "{name_en}",
+ "text-max-width": 7,
+ "text-size": { "base": 1, "stops": [ [ 7, 11.5 ], [ 15, 20 ] ] }
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1.25,
+ "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }
+ }
+ },
+ {
+ "id": "place-island",
+ "type": "symbol",
+ "source": "composite",
+ "source-layer": "place_label",
+ "maxzoom": 16,
+ "filter": [ "==", "type", "island" ],
+ "layout": {
+ "text-line-height": 1.2,
+ "text-size": { "base": 1, "stops": [ [ 10, 11 ], [ 18, 16 ] ] },
+ "text-max-angle": 38,
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ],
+ "text-padding": 2,
+ "text-offset": [ 0, 0 ],
+ "text-rotation-alignment": "viewport",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.01,
+ "text-max-width": 7
+ },
+ "paint": {
+ "text-color": "hsl(230, 29%, 35%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "place-city-sm",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444862510685.128" },
+ "source": "composite",
+ "source-layer": "place_label",
+ "maxzoom": 14,
+ "filter": [ "all", [ "!in", "scalerank", 0, 1, 2, 3, 4, 5 ], [ "==", "type", "city" ] ],
+ "layout": {
+ "text-size": { "base": 1, "stops": [ [ 6, 12 ], [ 14, 22 ] ] },
+ "icon-image": "dot-9",
+ "text-font": {
+ "base": 1,
+ "stops": [
+ [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ],
+ [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ]
+ ]
+ },
+ "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.2 ] ], [ 8, [ 0, 0 ] ] ] },
+ "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] },
+ "text-field": "{name_en}",
+ "text-max-width": 7
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1.25,
+ "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }
+ }
+ },
+ {
+ "id": "place-city-md-s",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444862510685.128" },
+ "source": "composite",
+ "source-layer": "place_label",
+ "maxzoom": 14,
+ "filter": [
+ "all",
+ [ "in", "ldir", "E", "S", "SE", "SW" ],
+ [ "in", "scalerank", 3, 4, 5 ],
+ [ "==", "type", "city" ]
+ ],
+ "layout": {
+ "text-field": "{name_en}",
+ "icon-image": "dot-10",
+ "text-anchor": { "base": 1, "stops": [ [ 7, "top" ], [ 8, "center" ] ] },
+ "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, 0.1 ] ], [ 8, [ 0, 0 ] ] ] },
+ "text-font": {
+ "base": 1,
+ "stops": [
+ [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ],
+ [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ]
+ ]
+ },
+ "text-size": { "base": 0.9, "stops": [ [ 5, 12 ], [ 12, 22 ] ] }
+ },
+ "paint": {
+ "text-halo-width": 1,
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-blur": 1,
+ "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] }
+ }
+ },
+ {
+ "id": "place-city-md-n",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444862510685.128" },
+ "source": "composite",
+ "source-layer": "place_label",
+ "maxzoom": 14,
+ "filter": [
+ "all",
+ [ "in", "ldir", "N", "NE", "NW", "W" ],
+ [ "in", "scalerank", 3, 4, 5 ],
+ [ "==", "type", "city" ]
+ ],
+ "layout": {
+ "icon-image": "dot-10",
+ "text-font": {
+ "base": 1,
+ "stops": [
+ [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ],
+ [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ]
+ ]
+ },
+ "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.25 ] ], [ 8, [ 0, 0 ] ] ] },
+ "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] },
+ "text-field": "{name_en}",
+ "text-max-width": 7,
+ "text-size": { "base": 0.9, "stops": [ [ 5, 12 ], [ 12, 22 ] ] }
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1,
+ "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] },
+ "text-halo-blur": 1
+ }
+ },
+ {
+ "id": "place-city-lg-s",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444862510685.128" },
+ "source": "composite",
+ "source-layer": "place_label",
+ "minzoom": 1,
+ "maxzoom": 14,
+ "filter": [
+ "all",
+ [ "in", "ldir", "E", "S", "SE", "SW" ],
+ [ "<=", "scalerank", 2 ],
+ [ "==", "type", "city" ]
+ ],
+ "layout": {
+ "icon-image": "dot-11",
+ "text-font": {
+ "base": 1,
+ "stops": [
+ [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ],
+ [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ]
+ ]
+ },
+ "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, 0.15 ] ], [ 8, [ 0, 0 ] ] ] },
+ "text-anchor": { "base": 1, "stops": [ [ 7, "top" ], [ 8, "center" ] ] },
+ "text-field": "{name_en}",
+ "text-max-width": 7,
+ "text-size": { "base": 0.9, "stops": [ [ 4, 12 ], [ 10, 22 ] ] }
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1,
+ "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] },
+ "text-halo-blur": 1
+ }
+ },
+ {
+ "id": "place-city-lg-n",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444862510685.128" },
+ "source": "composite",
+ "source-layer": "place_label",
+ "minzoom": 1,
+ "maxzoom": 14,
+ "filter": [
+ "all",
+ [ "in", "ldir", "N", "NE", "NW", "W" ],
+ [ "<=", "scalerank", 2 ],
+ [ "==", "type", "city" ]
+ ],
+ "layout": {
+ "icon-image": "dot-11",
+ "text-font": {
+ "base": 1,
+ "stops": [
+ [ 7, [ "DIN Offc Pro Regular", "Arial Unicode MS Regular" ] ],
+ [ 8, [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ] ]
+ ]
+ },
+ "text-offset": { "base": 1, "stops": [ [ 7.99, [ 0, -0.25 ] ], [ 8, [ 0, 0 ] ] ] },
+ "text-anchor": { "base": 1, "stops": [ [ 7, "bottom" ], [ 8, "center" ] ] },
+ "text-field": "{name_en}",
+ "text-max-width": 7,
+ "text-size": { "base": 0.9, "stops": [ [ 4, 12 ], [ 10, 22 ] ] }
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-opacity": 1,
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1,
+ "icon-opacity": { "base": 1, "stops": [ [ 7.99, 1 ], [ 8, 0 ] ] },
+ "text-halo-blur": 1
+ }
+ },
+ {
+ "id": "marine-label-sm-ln",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856087950.3635" },
+ "source": "composite",
+ "source-layer": "marine_label",
+ "minzoom": 3,
+ "maxzoom": 10,
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ ">=", "labelrank", 4 ] ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1, "stops": [ [ 3, 12 ], [ 6, 16 ] ] },
+ "symbol-spacing": { "base": 1, "stops": [ [ 4, 100 ], [ 6, 400 ] ] },
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "symbol-placement": "line",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.1,
+ "text-max-width": 5
+ },
+ "paint": { "text-color": "hsl(205, 83%, 88%)" }
+ },
+ {
+ "id": "marine-label-sm-pt",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856087950.3635" },
+ "source": "composite",
+ "source-layer": "marine_label",
+ "minzoom": 3,
+ "maxzoom": 10,
+ "filter": [ "all", [ "==", "$type", "Point" ], [ ">=", "labelrank", 4 ] ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-max-width": 5,
+ "text-letter-spacing": 0.1,
+ "text-line-height": 1.5,
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "text-size": { "base": 1, "stops": [ [ 3, 12 ], [ 6, 16 ] ] }
+ },
+ "paint": { "text-color": "hsl(205, 83%, 88%)" }
+ },
+ {
+ "id": "marine-label-md-ln",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856087950.3635" },
+ "source": "composite",
+ "source-layer": "marine_label",
+ "minzoom": 2,
+ "maxzoom": 8,
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ "in", "labelrank", 2, 3 ] ],
+ "layout": {
+ "text-line-height": 1.1,
+ "text-size": { "base": 1.1, "stops": [ [ 2, 12 ], [ 5, 20 ] ] },
+ "symbol-spacing": 250,
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "symbol-placement": "line",
+ "text-field": "{name_en}",
+ "text-letter-spacing": 0.15,
+ "text-max-width": 5
+ },
+ "paint": { "text-color": "hsl(205, 83%, 88%)" }
+ },
+ {
+ "id": "marine-label-md-pt",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856087950.3635" },
+ "source": "composite",
+ "source-layer": "marine_label",
+ "minzoom": 2,
+ "maxzoom": 8,
+ "filter": [ "all", [ "==", "$type", "Point" ], [ "in", "labelrank", 2, 3 ] ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-max-width": 5,
+ "text-letter-spacing": 0.15,
+ "text-line-height": 1.5,
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "text-size": { "base": 1.1, "stops": [ [ 2, 14 ], [ 5, 20 ] ] }
+ },
+ "paint": { "text-color": "hsl(205, 83%, 88%)" }
+ },
+ {
+ "id": "marine-label-lg-ln",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856087950.3635" },
+ "source": "composite",
+ "source-layer": "marine_label",
+ "minzoom": 1,
+ "maxzoom": 4,
+ "filter": [ "all", [ "==", "$type", "LineString" ], [ "==", "labelrank", 1 ] ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-max-width": 4,
+ "text-letter-spacing": 0.25,
+ "text-line-height": 1.1,
+ "symbol-placement": "line",
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "text-size": { "base": 1, "stops": [ [ 1, 14 ], [ 4, 30 ] ] }
+ },
+ "paint": { "text-color": "hsl(205, 83%, 88%)" }
+ },
+ {
+ "id": "marine-label-lg-pt",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856087950.3635" },
+ "source": "composite",
+ "source-layer": "marine_label",
+ "minzoom": 1,
+ "maxzoom": 4,
+ "filter": [ "all", [ "==", "$type", "Point" ], [ "==", "labelrank", 1 ] ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-max-width": 4,
+ "text-letter-spacing": 0.25,
+ "text-line-height": 1.5,
+ "text-font": [ "DIN Offc Pro Italic", "Arial Unicode MS Regular" ],
+ "text-size": { "base": 1, "stops": [ [ 1, 14 ], [ 4, 30 ] ] }
+ },
+ "paint": { "text-color": "hsl(205, 83%, 88%)" }
+ },
+ {
+ "id": "state-label-sm",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856151690.9143" },
+ "source": "composite",
+ "source-layer": "state_label",
+ "minzoom": 3,
+ "maxzoom": 9,
+ "filter": [ "<", "area", 20000 ],
+ "layout": {
+ "text-size": { "base": 1, "stops": [ [ 6, 10 ], [ 9, 14 ] ] },
+ "text-transform": "uppercase",
+ "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ],
+ "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 6, "{name_en}" ] ] },
+ "text-letter-spacing": 0.15,
+ "text-max-width": 5
+ },
+ "paint": {
+ "text-opacity": 1,
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "state-label-md",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856151690.9143" },
+ "source": "composite",
+ "source-layer": "state_label",
+ "minzoom": 3,
+ "maxzoom": 8,
+ "filter": [ "all", [ "<", "area", 80000 ], [ ">=", "area", 20000 ] ],
+ "layout": {
+ "text-size": { "base": 1, "stops": [ [ 5, 10 ], [ 8, 16 ] ] },
+ "text-transform": "uppercase",
+ "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ],
+ "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 5, "{name_en}" ] ] },
+ "text-letter-spacing": 0.15,
+ "text-max-width": 6
+ },
+ "paint": {
+ "text-opacity": 1,
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "state-label-lg",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856151690.9143" },
+ "source": "composite",
+ "source-layer": "state_label",
+ "minzoom": 3,
+ "maxzoom": 7,
+ "filter": [ ">=", "area", 80000 ],
+ "layout": {
+ "text-size": { "base": 1, "stops": [ [ 4, 10 ], [ 7, 18 ] ] },
+ "text-transform": "uppercase",
+ "text-font": [ "DIN Offc Pro Bold", "Arial Unicode MS Bold" ],
+ "text-padding": 1,
+ "text-field": { "base": 1, "stops": [ [ 0, "{abbr}" ], [ 4, "{name_en}" ] ] },
+ "text-letter-spacing": 0.15,
+ "text-max-width": 6
+ },
+ "paint": {
+ "text-opacity": 1,
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": "hsl(0, 0%, 100%)",
+ "text-halo-width": 1
+ }
+ },
+ {
+ "id": "country-label-sm",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856144497.7825" },
+ "source": "composite",
+ "source-layer": "country_label",
+ "minzoom": 1,
+ "maxzoom": 10,
+ "filter": [ ">=", "scalerank", 5 ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-max-width": 6,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-size": { "base": 0.9, "stops": [ [ 5, 14 ], [ 9, 22 ] ] }
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] },
+ "text-halo-width": 1.25
+ }
+ },
+ {
+ "id": "country-label-md",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856144497.7825" },
+ "source": "composite",
+ "source-layer": "country_label",
+ "minzoom": 1,
+ "maxzoom": 8,
+ "filter": [ "in", "scalerank", 3, 4 ],
+ "layout": {
+ "text-field": { "base": 1, "stops": [ [ 0, "{code}" ], [ 2, "{name_en}" ] ] },
+ "text-max-width": 6,
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-size": { "base": 1, "stops": [ [ 3, 10 ], [ 8, 24 ] ] }
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] },
+ "text-halo-width": 1.25
+ }
+ },
+ {
+ "id": "country-label-lg",
+ "type": "symbol",
+ "metadata": { "mapbox:group": "1444856144497.7825" },
+ "source": "composite",
+ "source-layer": "country_label",
+ "minzoom": 1,
+ "maxzoom": 7,
+ "filter": [ "in", "scalerank", 1, 2 ],
+ "layout": {
+ "text-field": "{name_en}",
+ "text-max-width": { "base": 1, "stops": [ [ 0, 5 ], [ 3, 6 ] ] },
+ "text-font": [ "DIN Offc Pro Medium", "Arial Unicode MS Regular" ],
+ "text-size": { "base": 1, "stops": [ [ 1, 10 ], [ 6, 24 ] ] }
+ },
+ "paint": {
+ "text-color": "hsl(0, 0%, 0%)",
+ "text-halo-color": { "base": 1, "stops": [ [ 2, "rgba(255,255,255,0.75)" ], [ 3, "hsl(0, 0%, 100%)" ] ] },
+ "text-halo-width": 1.25
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/benchmark/function/camera_function.benchmark.cpp b/benchmark/function/camera_function.benchmark.cpp
new file mode 100644
index 0000000000..26de5701db
--- /dev/null
+++ b/benchmark/function/camera_function.benchmark.cpp
@@ -0,0 +1,61 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/style/function/source_function.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+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);
+ if (stops.size() > 1) stops += ",";
+ stops += "[" + value + ", " + value + "]";
+ }
+ stops += "]";
+ return R"({"type": "exponential", "base": 2, "stops": )" + stops + "}";
+}
+
+static void Parse_CameraFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+
+ while (state.KeepRunning()) {
+ conversion::Error error;
+ state.PauseTiming();
+ auto doc = createFunctionJSON(stopCount);
+ state.ResumeTiming();
+ optional<CameraFunction<float>> result = conversion::convertJSON<CameraFunction<float>>(doc, error);
+ if (!result) {
+ state.SkipWithError(error.message.c_str());
+ }
+ }
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+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::convertJSON<CameraFunction<float>>(doc, error);
+ if (!function) {
+ state.SkipWithError(error.message.c_str());
+ }
+
+ while(state.KeepRunning()) {
+ float z = 24.0f * static_cast<float>(rand() % 100) / 100;
+ function->evaluate(z);
+ }
+
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+BENCHMARK(Parse_CameraFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+BENCHMARK(Evaluate_CameraFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+
diff --git a/benchmark/function/composite_function.benchmark.cpp b/benchmark/function/composite_function.benchmark.cpp
new file mode 100644
index 0000000000..e2545e6349
--- /dev/null
+++ b/benchmark/function/composite_function.benchmark.cpp
@@ -0,0 +1,69 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/benchmark/stub_geometry_tile_feature.hpp>
+
+#include <mbgl/style/function/composite_exponential_stops.hpp>
+#include <mbgl/style/function/composite_function.hpp>
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+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++) {
+ std::string zoom = std::to_string(24.0f / stopCount * outerStop);
+ std::string value = std::to_string(100.0f / stopCount * innerStop);
+
+ if (stops.size() > 1) stops += ",";
+ stops += R"([{"zoom":)" + zoom + R"(,"value":)" + value + "}, " + value + "]";
+ }
+ }
+ stops += "]";
+ return R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})";
+}
+
+static void Parse_CompositeFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+
+ while (state.KeepRunning()) {
+ conversion::Error error;
+ state.PauseTiming();
+ auto doc = createFunctionJSON(stopCount);
+ state.ResumeTiming();
+ optional<CompositeFunction<float>> result = conversion::convertJSON<style::CompositeFunction<float>>(doc, error);
+ if (!result) {
+ state.SkipWithError(error.message.c_str());
+ }
+ }
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+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::convertJSON<CompositeFunction<float>>(doc, error);
+ if (!function) {
+ state.SkipWithError(error.message.c_str());
+ }
+
+ while(state.KeepRunning()) {
+ float z = 24.0f * static_cast<float>(rand() % 100) / 100;
+ function->evaluate(z, StubGeometryTileFeature(PropertyMap { { "x", static_cast<int64_t>(rand() % 100) } }), -1.0f);
+ }
+
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+BENCHMARK(Parse_CompositeFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+BENCHMARK(Evaluate_CompositeFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+
diff --git a/benchmark/function/source_function.benchmark.cpp b/benchmark/function/source_function.benchmark.cpp
new file mode 100644
index 0000000000..af361943e3
--- /dev/null
+++ b/benchmark/function/source_function.benchmark.cpp
@@ -0,0 +1,63 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/benchmark/stub_geometry_tile_feature.hpp>
+
+#include <mbgl/style/function/source_function.hpp>
+
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
+#include <mbgl/style/conversion/function.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+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);
+ if (stops.size() > 1) stops += ",";
+ stops += "[" + value + ", " + value + "]";
+ }
+ stops += "]";
+ return R"({"type": "exponential", "base": 2, "stops": )" + stops + R"(, "property": "x"})";
+}
+
+static void Parse_SourceFunction(benchmark::State& state) {
+ size_t stopCount = state.range(0);
+
+ while (state.KeepRunning()) {
+ conversion::Error error;
+ state.PauseTiming();
+ auto doc = createFunctionJSON(stopCount);
+ state.ResumeTiming();
+ optional<SourceFunction<float>> result = conversion::convertJSON<SourceFunction<float>>(doc, error);
+ if (!result) {
+ state.SkipWithError(error.message.c_str());
+ }
+ }
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+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::convertJSON<SourceFunction<float>>(doc, error);
+ if (!function) {
+ state.SkipWithError(error.message.c_str());
+ }
+
+ while(state.KeepRunning()) {
+ function->evaluate(StubGeometryTileFeature(PropertyMap { { "x", static_cast<int64_t>(rand() % 100) } }), -1.0f);
+ }
+
+ state.SetLabel(std::to_string(stopCount).c_str());
+}
+
+BENCHMARK(Parse_SourceFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+BENCHMARK(Evaluate_SourceFunction)
+ ->Arg(1)->Arg(2)->Arg(4)->Arg(6)->Arg(8)->Arg(10)->Arg(12);
+
+
diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp
index d650cb72c9..4984668400 100644
--- a/benchmark/parse/filter.benchmark.cpp
+++ b/benchmark/parse/filter.benchmark.cpp
@@ -2,20 +2,16 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/filter.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
-#include <rapidjson/document.h>
-
using namespace mbgl;
style::Filter parse(const char* expression) {
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
- doc.Parse<0>(expression);
style::conversion::Error error;
- return *style::conversion::convert<style::Filter, JSValue>(doc, error);
+ return *style::conversion::convertJSON<style::Filter>(expression, error);
}
static void Parse_Filter(benchmark::State& state) {
diff --git a/benchmark/parse/tile_mask.benchmark.cpp b/benchmark/parse/tile_mask.benchmark.cpp
new file mode 100644
index 0000000000..79ab685c28
--- /dev/null
+++ b/benchmark/parse/tile_mask.benchmark.cpp
@@ -0,0 +1,38 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/algorithm/update_tile_masks.hpp>
+
+using namespace mbgl;
+
+class MaskedRenderable {
+public:
+ MaskedRenderable(const UnwrappedTileID& id_, TileMask&& mask_)
+ : id(id_), mask(std::move(mask_)) {
+ }
+
+ UnwrappedTileID id;
+ TileMask mask;
+ bool used = true;
+
+ void setMask(TileMask&& mask_) {
+ mask = std::move(mask_);
+ }
+};
+
+static void TileMaskGeneration(benchmark::State& state) {
+ std::vector<MaskedRenderable> renderables = {
+ MaskedRenderable{ UnwrappedTileID{ 12, 1028, 1456 }, {} },
+ MaskedRenderable{ UnwrappedTileID{ 13, 2056, 2912 }, {} },
+ MaskedRenderable{ UnwrappedTileID{ 13, 2056, 2913 }, {} },
+ MaskedRenderable{ UnwrappedTileID{ 14, 4112, 5824 }, {} },
+ MaskedRenderable{ UnwrappedTileID{ 14, 4112, 5827 }, {} },
+ MaskedRenderable{ UnwrappedTileID{ 14, 4114, 5824 }, {} },
+ MaskedRenderable{ UnwrappedTileID{ 14, 4114, 5825 }, {} },
+ };
+
+ while (state.KeepRunning()) {
+ algorithm::updateTileMasks<MaskedRenderable>({ renderables.begin(), renderables.end() });
+ }
+}
+
+BENCHMARK(TileMaskGeneration);
diff --git a/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp b/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp
new file mode 100644
index 0000000000..e27aeeb48b
--- /dev/null
+++ b/benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/util/feature.hpp>
+
+using namespace mbgl;
+
+class StubGeometryTileFeature : public GeometryTileFeature {
+public:
+ StubGeometryTileFeature(PropertyMap properties_)
+ : properties(std::move(properties_)) {
+ }
+
+ StubGeometryTileFeature(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_, PropertyMap properties_)
+ : properties(std::move(properties_)),
+ id(std::move(id_)),
+ type(type_),
+ geometry(std::move(geometry_)) {
+ }
+
+ PropertyMap properties;
+ optional<FeatureIdentifier> id;
+ FeatureType type = FeatureType::Point;
+ GeometryCollection geometry;
+
+ FeatureType getType() const override {
+ return type;
+ }
+
+ optional<FeatureIdentifier> getID() const override {
+ return id;
+ }
+
+ optional<Value> getValue(const std::string& key) const override {
+ return properties.count(key) ? properties.at(key) : optional<Value>();
+ }
+
+ GeometryCollection getGeometries() const override {
+ return geometry;
+ }
+};
diff --git a/benchmark/src/mbgl/benchmark/util.cpp b/benchmark/src/mbgl/benchmark/util.cpp
deleted file mode 100644
index 87d68ea729..0000000000
--- a/benchmark/src/mbgl/benchmark/util.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <mbgl/benchmark/util.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
-
-#include <mbgl/map/map.hpp>
-#include <mbgl/map/view.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/util/run_loop.hpp>
-
-namespace mbgl {
-namespace benchmark {
-
-void render(Map& map, OffscreenView& view) {
- PremultipliedImage result;
- map.renderStill(view, [&](std::exception_ptr) {
- result = view.readStillImage();
- });
-
- while (!result.valid()) {
- util::RunLoop::Get()->runOnce();
- }
-}
-
-} // namespace benchmark
-} // namespace mbgl
diff --git a/benchmark/src/mbgl/benchmark/util.hpp b/benchmark/src/mbgl/benchmark/util.hpp
deleted file mode 100644
index 73acfb69d5..0000000000
--- a/benchmark/src/mbgl/benchmark/util.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-namespace mbgl {
-
-class Map;
-class OffscreenView;
-
-namespace benchmark {
-
-void render(Map&, OffscreenView&);
-
-} // namespace benchmark
-} // namespace mbgl
diff --git a/benchmark/util/dtoa.benchmark.cpp b/benchmark/util/dtoa.benchmark.cpp
new file mode 100644
index 0000000000..0009c6dd15
--- /dev/null
+++ b/benchmark/util/dtoa.benchmark.cpp
@@ -0,0 +1,66 @@
+#include <benchmark/benchmark.h>
+
+#include <mbgl/util/dtoa.hpp>
+
+#include <cfloat>
+#include <cmath>
+
+using namespace mbgl;
+
+static void Util_dtoa(::benchmark::State& state) {
+ while (state.KeepRunning()) {
+ util::dtoa(0.);
+ util::dtoa(M_E);
+ util::dtoa(M_LOG2E);
+ util::dtoa(M_LOG10E);
+ util::dtoa(M_LN2);
+ util::dtoa(M_LN10);
+ util::dtoa(M_PI);
+ util::dtoa(M_PI_2);
+ util::dtoa(M_PI_4);
+ util::dtoa(M_1_PI);
+ util::dtoa(M_2_PI);
+ util::dtoa(M_2_SQRTPI);
+ util::dtoa(M_SQRT2);
+ util::dtoa(M_SQRT1_2);
+ }
+}
+
+static void Util_standardDtoa(::benchmark::State& state) {
+ while (state.KeepRunning()) {
+ std::to_string(0.);
+ std::to_string(M_E);
+ std::to_string(M_LOG2E);
+ std::to_string(M_LOG10E);
+ std::to_string(M_LN2);
+ std::to_string(M_LN10);
+ std::to_string(M_PI);
+ std::to_string(M_PI_2);
+ std::to_string(M_PI_4);
+ std::to_string(M_1_PI);
+ std::to_string(M_2_PI);
+ std::to_string(M_2_SQRTPI);
+ std::to_string(M_SQRT2);
+ std::to_string(M_SQRT1_2);
+ }
+}
+
+static void Util_dtoaLimits(::benchmark::State& state) {
+ while (state.KeepRunning()) {
+ util::dtoa(DBL_MIN);
+ util::dtoa(DBL_MAX);
+ }
+}
+
+static void Util_standardDtoaLimits(::benchmark::State& state) {
+ while (state.KeepRunning()) {
+ std::to_string(DBL_MIN);
+ std::to_string(DBL_MAX);
+ }
+}
+
+BENCHMARK(Util_dtoa);
+BENCHMARK(Util_standardDtoa);
+
+BENCHMARK(Util_dtoaLimits);
+BENCHMARK(Util_standardDtoaLimits);
diff --git a/bin/offline.cpp b/bin/offline.cpp
index ec2fb09096..5b403a6416 100644
--- a/bin/offline.cpp
+++ b/bin/offline.cpp
@@ -4,53 +4,63 @@
#include <mbgl/storage/default_file_source.hpp>
+#include <args/args.hxx>
+
#include <cstdlib>
#include <iostream>
#include <csignal>
#include <atomic>
+#include <sstream>
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunknown-pragmas"
-#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
-#pragma GCC diagnostic ignored "-Wshadow"
-#include <boost/program_options.hpp>
-#pragma GCC diagnostic pop
-
-namespace po = boost::program_options;
using namespace std::literals::chrono_literals;
int main(int argc, char *argv[]) {
- std::string style = mbgl::util::default_styles::streets.url;
- double north = 37.2, west = -122.8, south = 38.1, east = -121.7; // Bay area
- double minZoom = 0.0, maxZoom = 15.0, pixelRatio = 1.0;
- std::string output = "offline.db";
+ args::ArgumentParser argumentParser("Mapbox GL offline tool");
+ args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "help"});
- const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN");
- std::string token = tokenEnv ? tokenEnv : std::string();
-
- po::options_description desc("Allowed options");
- desc.add_options()
- ("style,s", po::value(&style)->value_name("URL"), "Map stylesheet")
- ("north", po::value(&north)->value_name("degrees")->default_value(north), "North latitude")
- ("west", po::value(&west)->value_name("degrees")->default_value(west), "West longitude")
- ("south", po::value(&south)->value_name("degrees")->default_value(south), "South latitude")
- ("east", po::value(&east)->value_name("degrees")->default_value(east), "East longitude")
- ("minZoom", po::value(&minZoom)->value_name("number")->default_value(minZoom), "Min zoom level")
- ("maxZoom", po::value(&maxZoom)->value_name("number")->default_value(maxZoom), "Max zoom level")
- ("pixelRatio", po::value(&pixelRatio)->value_name("number")->default_value(pixelRatio), "Pixel ratio")
- ("token,t", po::value(&token)->value_name("key")->default_value(token), "Mapbox access token")
- ("output,o", po::value(&output)->value_name("file")->default_value(output), "Output database file name")
- ;
+ args::ValueFlag<std::string> tokenValue(argumentParser, "key", "Mapbox access token", {'t', "token"});
+ args::ValueFlag<std::string> styleValue(argumentParser, "URL", "Map stylesheet", {'s', "style"});
+ args::ValueFlag<std::string> outputValue(argumentParser, "file", "Output database file name", {'o', "output"});
+
+ args::ValueFlag<double> northValue(argumentParser, "degrees", "North latitude", {"north"});
+ args::ValueFlag<double> westValue(argumentParser, "degrees", "West longitude", {"west"});
+ args::ValueFlag<double> southValue(argumentParser, "degrees", "South latitude", {"south"});
+ args::ValueFlag<double> eastValue(argumentParser, "degrees", "East longitude", {"east"});
+ args::ValueFlag<double> minZoomValue(argumentParser, "number", "Min zoom level", {"minZoom"});
+ args::ValueFlag<double> maxZoomValue(argumentParser, "number", "Max zoom level", {"maxZoom"});
+ args::ValueFlag<double> pixelRatioValue(argumentParser, "number", "Pixel ratio", {"pixelRatio"});
try {
- po::variables_map vm;
- po::store(po::parse_command_line(argc, argv, desc), vm);
- po::notify(vm);
- } catch(std::exception& e) {
- std::cout << "Error: " << e.what() << std::endl << desc;
+ argumentParser.ParseCLI(argc, argv);
+ } catch (args::Help) {
+ std::cout << argumentParser;
+ exit(0);
+ } catch (args::ParseError e) {
+ std::cerr << e.what() << std::endl;
+ std::cerr << argumentParser;
exit(1);
+ } catch (args::ValidationError e) {
+ std::cerr << e.what() << std::endl;
+ std::cerr << argumentParser;
+ exit(2);
}
+ std::string style = styleValue ? args::get(styleValue) : mbgl::util::default_styles::streets.url;
+
+ // Bay area
+ const double north = northValue ? args::get(northValue) : 37.2;
+ const double west = westValue ? args::get(westValue) : -122.8;
+ const double south = southValue ? args::get(southValue) : 38.1;
+ const double east = eastValue ? args::get(eastValue) : -121.7;
+
+ const double minZoom = minZoomValue ? args::get(minZoomValue) : 0.0;
+ const double maxZoom = maxZoomValue ? args::get(maxZoomValue) : 15.0;
+ const double pixelRatio = pixelRatioValue ? args::get(pixelRatioValue) : 1.0;
+ const std::string output = outputValue ? args::get(outputValue) : "offline.db";
+
+ const char* tokenEnv = getenv("MAPBOX_ACCESS_TOKEN");
+ const std::string token = tokenValue ? args::get(tokenValue) : (tokenEnv ? tokenEnv : std::string());
+
using namespace mbgl;
util::RunLoop loop;
diff --git a/bin/render.cpp b/bin/render.cpp
index 51c6faef56..6ceb32eb00 100644
--- a/bin/render.cpp
+++ b/bin/render.cpp
@@ -1,100 +1,96 @@
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/default_styles.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.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));
}
- HeadlessBackend backend;
- BackendScope scope { backend };
- OffscreenView view(backend.getContext(), { static_cast<uint32_t>(width * pixelRatio),
- static_cast<uint32_t>(height * pixelRatio) });
ThreadPool threadPool(4);
- Map map(backend, mbgl::Size { width, height }, pixelRatio, fileSource, threadPool, MapMode::Still);
+ HeadlessFrontend frontend({ width, height }, pixelRatio, fileSource, threadPool);
+ 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);
@@ -103,23 +99,14 @@ int main(int argc, char *argv[]) {
map.setDebug(debug ? mbgl::MapDebugOptions::TileBorders | mbgl::MapDebugOptions::ParseStatus : mbgl::MapDebugOptions::NoDebug);
}
- map.renderStill(view, [&](std::exception_ptr error) {
- try {
- if (error) {
- std::rethrow_exception(error);
- }
- } catch(std::exception& e) {
- std::cout << "Error: " << e.what() << std::endl;
- exit(1);
- }
-
+ try {
std::ofstream out(output, std::ios::binary);
- out << encodePNG(view.readStillImage());
+ out << encodePNG(frontend.render(map));
out.close();
- loop.stop();
- });
-
- loop.run();
+ } catch(std::exception& e) {
+ std::cout << "Error: " << e.what() << std::endl;
+ exit(1);
+ }
return 0;
}
diff --git a/circle.yml b/circle.yml
index 9354a6d854..4a1c22c305 100644
--- a/circle.yml
+++ b/circle.yml
@@ -4,69 +4,258 @@ workflows:
version: 2
default:
jobs:
- - clang-tidy
+ - clang-tidy:
+ filters:
+ branches:
+ ignore: master
- android-debug-arm-v7
- android-release-all
- - node4-clang39-release
- - node6-clang39-release
- - node6-clang39-debug
- - linux-clang39-debug
+ - node4-clang39-release:
+ filters:
+ tags:
+ only: /node-.*/
+ - node6-clang39-release:
+ filters:
+ tags:
+ only: /node-.*/
+ - node6-gcc6-debug:
+ filters:
+ tags:
+ only: /node-.*/
+ - linux-clang-3.8-libcxx-debug
+ - linux-clang4-sanitize-address
+ - linux-clang4-sanitize-undefined
+ - linux-clang4-sanitize-thread
+ - linux-gcc4.9-debug
- linux-gcc5-debug-coverage
- linux-gcc5-release-qt4
- linux-gcc5-release-qt5
+ - ios-debug
+ #- ios-sanitize-address
+ - ios-sanitize-thread
+ - macos-debug
+ - macos-debug-qt5
+ - macos-release-node4:
+ filters:
+ tags:
+ only: /node-.*/
+ - macos-release-node6:
+ filters:
+ tags:
+ only: /node-.*/
+
+step-library:
+ - &generate-cache-key
+ run:
+ name: Generate cache key
+ command: |
+ echo "$(date +"%Y-%V")" > .circle-week
+ ccache --clear
+ ccache --max-size=5G
+ - &restore-cache
+ restore_cache:
+ keys:
+ - 'v3/{{ .Environment.CIRCLE_JOB }}/{{ arch }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
+ - 'v3/{{ .Environment.CIRCLE_JOB }}/{{ arch }}/master/{{ checksum ".circle-week" }}'
+ - &save-cache
+ save_cache:
+ key: 'v3/{{ .Environment.CIRCLE_JOB }}/{{ arch }}/{{ .Branch }}/{{ checksum ".circle-week" }}'
+ paths: [ "node_modules", "/root/.ccache", "~/.ccache", "mason_packages/.binaries" ]
+
+
+ - &restore-gradle-cache
+ restore_cache:
+ keys:
+ - 'v3/{{ checksum "platform/android/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/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
+ command: |
+ ccache --zero-stats
+ ccache --show-stats
+ - &show-ccache-stats
+ run:
+ name: Show ccache statistics
+ command: ccache --show-stats
+
+
+ - &setup-llvm-symbolizer
+ run:
+ name: Environment Setup
+ command: |
+ # LLVM has a hard check for "llvm-symbolizer" and doesn't support suffixed executables
+ ln -s /usr/bin/llvm-symbolizer-* /usr/bin/llvm-symbolizer
+ # We'll use tee to redirect stderr to a file so we can check for sanitiziation
+ # https://bugs.launchpad.net/ubuntu/+source/xorg-server/+bug/1059947
+ sed -i 's/"$@" 2>&1/"$@"/' /usr/bin/xvfb-run
+
+
+ - &build-node
+ run:
+ name: Build node
+ command: make node
+ - &build-linux
+ run:
+ name: Build linux
+ command: make linux
+ - &build-benchmark
+ run:
+ name: Build benchmark
+ command: make benchmark
+ - &build-test
+ run:
+ name: Build test
+ command: make test
+ - &build-qt-app
+ run:
+ name: Build qt-app
+ command: make qt-app
+ - &build-qt-test
+ run:
+ name: Build qt-test
+ command: make qt-test
+ - &build-ios-test
+ run:
+ name: Build ios-test
+ command: make ios-test
+ - &build-macos-test
+ run:
+ name: Build and run macOS tests
+ command: make run-test
+
+
+ - &check-public-symbols
+ run:
+ name: Check public symbols
+ command: make check-public-symbols
+
+
+ - &install-macos-dependencies
+ run:
+ name: Install macOS dependencies
+ command: |
+ brew install cmake
+ brew install ccache
+
+ - &install-macos-node4-dependencies
+ run:
+ name: Install macOS Node@4 dependencies
+ command: |
+ brew install node@4
+ brew link node@4 --force --overwrite
+
+ - &install-macos-node6-dependencies
+ run:
+ name: Install macOS Node@6 dependencies
+ command: |
+ brew install node@6
+ brew link node@6 --force --overwrite
+
+ - &install-macos-qt-dependencies
+ 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
+
+ - &run-node-macos-tests
+ run:
+ name: Run node tests
+ command: make test-node
+
+ - &run-node-linux-tests
+ run:
+ name: Run node tests
+ command: |
+ xvfb-run --server-args="-screen 0 1024x768x24" \
+ logbt -- apitrace trace --api=egl -v make test-node
+
+ - &run-node-linux-tests-recycle-map
+ run:
+ name: Run node tests (recycling the map object)
+ command: |
+ xvfb-run --server-args="-screen 0 1024x768x24" \
+ logbt -- apitrace trace --api=egl -v make test-node-recycle-map
+
+ - &run-unit-tests
+ run:
+ name: Run tests
+ command: |
+ xvfb-run --server-args="-screen 0 1024x768x24" \
+ make run-test
-jobs:
+ - &publish-node-package
+ run:
+ name: Publish node package
+ when: on_success
+ command: platform/node/scripts/after_success.sh
+
+
+ - &upload-render-tests
+ store_artifacts:
+ path: mapbox-gl-js/test/integration/render-tests/index.html
+ destination: render-tests
+ - &upload-render-tests-recycle-map
+ store_artifacts:
+ path: mapbox-gl-js/test/integration/render-tests/index-recycle-map.html
+ destination: render-tests
+
+jobs:
# ------------------------------------------------------------------------------
clang-tidy:
docker:
- - image: mbgl/ci:r3-linux-clang-3.9
+ - image: mbgl/7d2403f42e:linux-clang-3.9
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Debug
- branches:
- ignore:
- - master
steps:
- checkout
- - restore_cache:
- key: v1-clang-tidy
- paths:
- - node_modules
- - /root/.ccache
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
- run:
name: Fetch 'origin/master' branch
command: git fetch origin master:refs/remotes/origin/master
- run:
name: Generate compilation database
command: make compdb
+ - *show-ccache-stats
+ - *save-cache
- run:
name: Run Clang checks
command: make check
- - save_cache:
- key: v1-clang-tidy
- paths:
- - node_modules
- - /root/.ccache
# ------------------------------------------------------------------------------
android-debug-arm-v7:
docker:
- - image: mbgl/ci:r3-android-ndk-r15-gradle
+ - image: mbgl/7d2403f42e:android-ndk-r16b
+ resource_class: large
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Debug
+ IS_LOCAL_DEVELOPMENT: false
steps:
- checkout
- - restore_cache:
- key: v1-android-debug-arm-v7
- paths:
- - node_modules
- - /root/.ccache
+ - *generate-cache-key
+ - *restore-cache
+ - *restore-gradle-cache
+ - *reset-ccache-stats
- run:
name: Build libmapbox-gl.so for arm-v7
command: make android-lib-arm-v7
@@ -77,19 +266,27 @@ jobs:
name: Test phone module
command: make run-android-unit-test
- run:
- name: Test wear module
- command: make run-android-wear-unit-test
- - run:
name: Generate Espresso sanity tests
command: make test-code-android
- run:
name: Check Java code style
command: make android-checkstyle
- run:
+ name: Check Android modules for potential bugs (Lint SDK)
+ command: |
+ make android-lint-sdk
+ - run:
+ name: Check Android modules for potential bugs (Lint Test App)
+ command: |
+ make android-lint-test-app
+ - run:
name: Build Test APK
command: |
echo "${MAPBOX_DEVELOPER_CONFIG_XML}" > platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml
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
@@ -99,49 +296,58 @@ jobs:
rm secret.json
- run:
name: Run instrumentation tests on Firebase
+ no_output_timeout: 1200
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 15m \
- --test-targets "class com.mapbox.mapboxsdk.testapp.maps.widgets.LogoTest" 2>&1 | tee firebase.log) || EXIT_CODE=$?
+ --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 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
+ gsutil -m cp -n -R -Z "$FIREBASE_TEST_BUCKET*" platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug
echo "Try running ndk-stack on downloaded logcat to symbolicate the stacktraces:"
- find platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk -type f -name "logcat" -print0 | \
+ find platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug -type f -name "logcat" -print0 | \
xargs -0 -I '{}' ${ANDROID_NDK_HOME}/ndk-stack -sym build/android-arm-v7/Debug -dump {}
exit ${EXIT_CODE:-0}
- - save_cache:
- key: v1-android-debug-arm-v7
- paths:
- - node_modules
- - /root/.ccache
- 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
+ - store_artifacts:
+ path: platform/android/MapboxGLAndroidSDK/build/reports/lint-results.xml
+ - store_artifacts:
+ path: platform/android/MapboxGLAndroidSDK/lint-baseline.xml
+ - store_artifacts:
+ path: platform/android/MapboxGLAndroidSDKTestApp/build/reports/lint-results.html
+ - store_artifacts:
+ path: platform/android/MapboxGLAndroidSDKTestApp/build/reports/lint-results.xml
+ - store_artifacts:
+ path: platform/android/MapboxGLAndroidSDKTestApp/lint-baseline.xml
# ------------------------------------------------------------------------------
android-release-all:
docker:
- - image: mbgl/ci:r3-android-ndk-r15-gradle
+ - image: mbgl/7d2403f42e:android-ndk-r16b
+ resource_class: large
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Release
+ IS_LOCAL_DEVELOPMENT: false
steps:
- checkout
- - restore_cache:
- key: v1-android-release-all
- paths:
- - node_modules
- - /root/.ccache
+ - *generate-cache-key
+ - *restore-cache
+ - *restore-gradle-cache
+ - *reset-ccache-stats
- run:
name: Generate Maven credentials
shell: /bin/bash -euo pipefail
@@ -155,8 +361,11 @@ jobs:
- run:
name: Build package
command: make apackage
+ - *show-ccache-stats
+ - *save-cache
+ - *save-gradle-cache
- store_artifacts:
- path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk
+ path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/release
destination: .
- deploy:
name: Show statistics
@@ -166,311 +375,446 @@ jobs:
- deploy:
name: Publish to Maven
command: |
- if [ "${CIRCLE_BRANCH}" == "release-ios-v3.6.0-android-v5.1.0" ]; then make run-android-upload-archives ; fi
- - save_cache:
- key: v1-android-release-all
- paths:
- - node_modules
- - /root/.ccache
+ if [ "${CIRCLE_BRANCH}" == "master" ]; then make run-android-upload-archives ; fi
+
# ------------------------------------------------------------------------------
node4-clang39-release:
docker:
- - image: mbgl/ci:r3-linux-clang-3.9-node-4
+ - image: mbgl/7d2403f42e:linux-clang-3.9-node-4
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
- BUILDTYPE: Release
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: RelWithDebInfo
WITH_EGL: 1
- PACKAGE_JSON_VERSION: $(node -e "console.log(require('./package.json').version)")
- PUBLISH: $([[ "${CIRCLE_BRANCH}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
- DISPLAY: :0
+ WITH_CXX11ABI: 0
steps:
- checkout
- - restore_cache:
- key: v1-node4-clang39-release
- paths:
- - node_modules
- - /root/.ccache
- - run:
- name: Build node
- command: make node
- - run:
- name: Run node tests
- command: |
- source scripts/circle_setup.sh
- mapbox_install_logbt
- mapbox_install_apitrace
- mapbox_export_mesa_library_path
- xvfb-run --server-args="-screen 0 1024x768x24" \
- ./logbt -- apitrace trace --api=egl -v make test-node
- - run:
- name: Publish node package
- when: on_success
- command: platform/node/scripts/after_success.sh
- - save_cache:
- key: v1-node4-clang39-release
- paths:
- - node_modules
- - /root/.ccache
- - store_artifacts:
- path: mapbox-gl-js/test/integration/render-tests/index.html
- destination: render-tests
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-node
+ - *show-ccache-stats
+ - *save-cache
+ - *run-node-linux-tests
+ - *publish-node-package
+ - *upload-render-tests
# ------------------------------------------------------------------------------
node6-clang39-release:
docker:
- - image: mbgl/ci:r3-linux-clang-3.9
+ - image: mbgl/7d2403f42e:linux-clang-3.9
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
- BUILDTYPE: Release
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: RelWithDebInfo
WITH_EGL: 1
- PACKAGE_JSON_VERSION: $(node -e "console.log(require('./package.json').version)")
- PUBLISH: $([[ "${CIRCLE_BRANCH}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
- DISPLAY: :0
+ WITH_CXX11ABI: 0
steps:
- checkout
- - restore_cache:
- key: v1-node6-clang39-release
- paths:
- - node_modules
- - /root/.ccache
- - run:
- name: Build node
- command: make node
- - run:
- name: Run node tests
- command: |
- source scripts/circle_setup.sh
- mapbox_install_logbt
- mapbox_install_apitrace
- mapbox_export_mesa_library_path
- xvfb-run --server-args="-screen 0 1024x768x24" \
- ./logbt -- apitrace trace --api=egl -v make test-node
- - run:
- name: Publish node package
- when: on_success
- command: platform/node/scripts/after_success.sh
- - save_cache:
- key: v1-node6-clang39-release
- paths:
- - node_modules
- - /root/.ccache
- - store_artifacts:
- path: mapbox-gl-js/test/integration/render-tests/index.html
- destination: render-tests
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-node
+ - *show-ccache-stats
+ - *save-cache
+ - *run-node-linux-tests
+ - *publish-node-package
+ - *upload-render-tests
# ------------------------------------------------------------------------------
- node6-clang39-debug:
+ node6-gcc6-debug:
docker:
- - image: mbgl/ci:r3-linux-clang-3.9
+ - image: mbgl/7d2403f42e:linux-gcc-6
+ resource_class: large
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
- PACKAGE_JSON_VERSION: $(node -e "console.log(require('./package.json').version)")
- PUBLISH: $([[ "${CIRCLE_BRANCH}" == "node-v${PACKAGE_JSON_VERSION}" ]] && echo true)
- DISPLAY: :0
steps:
- checkout
- - restore_cache:
- key: v1-node6-clang39-debug
- paths:
- - node_modules
- - /root/.ccache
- - run:
- name: Build node
- command: make node
- - run:
- name: Run node tests
- command: |
- source scripts/circle_setup.sh
- mapbox_install_logbt
- mapbox_install_apitrace
- mapbox_export_mesa_library_path
- xvfb-run --server-args="-screen 0 1024x768x24" \
- ./logbt -- apitrace trace --api=egl -v make test-node
- - run:
- name: Publish node package
- when: on_success
- command: platform/node/scripts/after_success.sh
- - save_cache:
- key: v1-node6-clang39-debug
- paths:
- - node_modules
- - /root/.ccache
- - store_artifacts:
- path: mapbox-gl-js/test/integration/render-tests/index.html
- destination: render-tests
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-node
+ - *show-ccache-stats
+ - *save-cache
+ - *run-node-linux-tests-recycle-map
+ - *publish-node-package
+ - *upload-render-tests-recycle-map
# ------------------------------------------------------------------------------
- linux-clang39-debug:
+ linux-clang-3.8-libcxx-debug:
docker:
- - image: mbgl/ci:r3-linux-clang-3.9
+ - image: mbgl/7d2403f42e:linux-clang-3.8-libcxx
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 6
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
- DISPLAY: :0
+ WITH_CXX11ABI: 1
steps:
- checkout
- - restore_cache:
- key: v1-linux-clang39-debug
- paths:
- - node_modules
- - /root/.ccache
- - run:
- name: Build linux
- command: make linux
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-linux
+ - *show-ccache-stats
+ - *save-cache
+
+# ------------------------------------------------------------------------------
+ linux-clang4-sanitize-address:
+ docker:
+ - image: mbgl/7d2403f42e:linux-clang-4
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: Sanitize
+ WITH_EGL: 1
+ GDB: '' # Do not run with GDB
+ CXXFLAGS: -fsanitize=address
+ LDFLAGS: -fsanitize=address
+ ASAN_OPTIONS: detect_leaks=0:color=always:print_summary=1
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *setup-llvm-symbolizer
+ - *build-test
+ - *show-ccache-stats
+ - *save-cache
- run:
- name: Build benchmark
- command: make benchmark
+ name: Run tests
+ command: |
+ xvfb-run --server-args="-screen 0 1024x768x24" make run-test 2> >(tee sanitizer 1>&2)
+ # Unfortunately, Google Test eats the status code, so we'll have to check the output.
+ [ -z "$(sed -n '/^SUMMARY: AddressSanitizer:/p' sanitizer)" ]
+
+# ------------------------------------------------------------------------------
+ linux-clang4-sanitize-undefined:
+ docker:
+ - image: mbgl/7d2403f42e:linux-clang-4
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: Sanitize
+ WITH_EGL: 1
+ GDB: '' # Do not run with GDB
+ CXXFLAGS: -fsanitize=undefined
+ LDFLAGS: -fsanitize=undefined
+ UBSAN_OPTIONS: print_stacktrace=1:color=always:print_summary=1
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *setup-llvm-symbolizer
+ - *build-test
+ - *show-ccache-stats
+ - *save-cache
- run:
- name: Build test
- command: make test
+ name: Run tests
+ command: |
+ xvfb-run --server-args="-screen 0 1024x768x24" make run-test 2> >(tee sanitizer 1>&2)
+ # Unfortunately, Google Test eats the status code, so we'll have to check the output.
+ [ -z "$(sed -n '/^SUMMARY: UndefinedBehaviorSanitizer:/p' sanitizer)" ]
+
+# ------------------------------------------------------------------------------
+ linux-clang4-sanitize-thread:
+ docker:
+ - image: mbgl/7d2403f42e:linux-clang-4
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: Sanitize
+ WITH_EGL: 1
+ GDB: '' # Do not run with GDB
+ CXXFLAGS: -fsanitize=thread
+ LDFLAGS: -fsanitize=thread
+ TSAN_OPTIONS: color=always:print_summary=1
+ steps:
+ - checkout
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *setup-llvm-symbolizer
+ - *build-test
+ - *show-ccache-stats
+ - *save-cache
- run:
name: Run tests
command: |
- source scripts/circle_setup.sh
- mapbox_export_mesa_library_path
- xvfb-run --server-args="-screen 0 1024x768x24" \
- make run-test
- - save_cache:
- key: v1-linux-clang39-debug
- paths:
- - node_modules
- - /root/.ccache
+ xvfb-run --server-args="-screen 0 1024x768x24" make run-test 2> >(tee sanitizer 1>&2)
+ # Unfortunately, Google Test eats the status code, so we'll have to check the output.
+ [ -z "$(sed -n '/^SUMMARY: ThreadSanitizer:/p' sanitizer)" ]
+
+# ------------------------------------------------------------------------------
+ linux-gcc4.9-debug:
+ docker:
+ - image: mbgl/7d2403f42e:linux-gcc-4.9
+ resource_class: large
+ working_directory: /src
+ environment:
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
+ BUILDTYPE: Debug
+ WITH_EGL: 1
+ WITH_CXX11ABI: 0
+ DISPLAY: :0
+ 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-gcc5-debug-coverage:
docker:
- - image: mbgl/ci:r3-linux-gcc-5
+ - image: mbgl/7d2403f42e:linux-gcc-5
+ resource_class: large
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 2
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Debug
WITH_EGL: 1
WITH_COVERAGE: 1
- DISPLAY: :0
steps:
- checkout
- - restore_cache:
- key: v1-linux-gcc5-debug-coverage
- paths:
- - node_modules
- - /root/.ccache
- - run:
- name: Build linux
- command: make linux
- - run:
- name: Build benchmark
- command: make benchmark
- - run:
- name: Build test
- command: make test
- - run:
- name: Run tests
- command: |
- source scripts/circle_setup.sh
- mapbox_export_mesa_library_path
- xvfb-run --server-args="-screen 0 1024x768x24" \
- make run-test
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-linux
+ - *build-benchmark
+ - *build-test
+ - *show-ccache-stats
+ - *save-cache
+ - *run-unit-tests
- run:
name: Upload coverage results to coveralls
command: |
- source scripts/circle_setup.sh
platform/linux/scripts/coveralls.sh
- - save_cache:
- key: v1-linux-gcc5-debug-coverage
- paths:
- - node_modules
- - /root/.ccache
# ------------------------------------------------------------------------------
linux-gcc5-release-qt4:
docker:
- - image: mbgl/ci:r3-linux-gcc-5-qt-4
+ - image: mbgl/7d2403f42e:linux-gcc-5-qt-4
+ resource_class: large
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 2 # OOM, causing the compiler to crash.
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Release
GTEST_OUTPUT: xml
LD_PRELOAD: /usr/lib/x86_64-linux-gnu/libjemalloc.so
- DISPLAY: 0
steps:
- checkout
- - restore_cache:
- key: v1-linux-gcc5-release-qt4
- paths:
- - node_modules
- - /root/.ccache
- - run:
- name: Build qt-app
- command: make qt-app
- - run:
- name: Build qt-test
- command: make qt-test
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-qt-app
+ - *build-qt-test
+ - *show-ccache-stats
+ - *save-cache
- run:
name: Run memory-load tests
command: |
- source scripts/circle_setup.sh
- mapbox_export_mesa_library_path
xvfb-run --server-args="-screen 0 1024x768x24" \
make run-qt-test-Memory.*:*.Load
scripts/log_memory_benchmarks.sh test_detail.xml "Platform=Linux,Compiler=${_CC},Arch=$(uname -m)"
- - save_cache:
- key: v1-linux-gcc5-release-qt4
- paths:
- - node_modules
- - /root/.ccache
# ------------------------------------------------------------------------------
linux-gcc5-release-qt5:
docker:
- - image: mbgl/ci:r3-linux-gcc-5-qt-5
+ - image: mbgl/7d2403f42e:linux-gcc-5-qt-5.9
+ resource_class: large
working_directory: /src
environment:
- LIBSYSCONFCPUS: 6
- JOBS: 2 # OOM, causing the compiler to crash.
+ LIBSYSCONFCPUS: 4
+ JOBS: 4
BUILDTYPE: Release
WITH_QT_I18N: 1
- DISPLAY: 0
steps:
- checkout
- - restore_cache:
- key: v1-linux-gcc5-release-qt5
- paths:
- - node_modules
- - /root/.ccache
- - run:
- name: Build qt-app
- command: make qt-app
- - run:
- name: Build qt-test
- command: make qt-test
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-qt-app
+ - *build-qt-test
- run:
name: Build qt-docs
command: make qt-docs
+ - *show-ccache-stats
+ - *save-cache
- run:
name: Run valgrind-backed tests
environment:
JOBS: 1 # https://github.com/mapbox/mapbox-gl-native/issues/9108
command: |
- source scripts/circle_setup.sh
- mapbox_export_mesa_library_path
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
- - save_cache:
- key: v1-linux-gcc5-release-qt5
- paths:
- - node_modules
- - /root/.ccache
+ build/qt-linux-x86_64/Release/mbgl-test --gtest_filter=-*.Load --gtest_filter=-Memory.Vector
+
+# ------------------------------------------------------------------------------
+ ios-debug:
+ macos:
+ xcode: "9.0"
+ environment:
+ BUILDTYPE: Debug
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ steps:
+ - checkout
+ - *install-macos-dependencies
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-ios-test
+ - *check-public-symbols
+ - run:
+ name: Lint plist files
+ command: make ios-lint
+ - *show-ccache-stats
+ - *save-cache
+
+# ------------------------------------------------------------------------------
+ ios-sanitize-address:
+ macos:
+ xcode: "9.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 address sanitizer
+ command: make ios-sanitize-address
+ - *show-ccache-stats
+ - *save-cache
+
+# ------------------------------------------------------------------------------
+ ios-sanitize-thread:
+ macos:
+ xcode: "9.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 sanitizer
+ command: make ios-sanitize-thread
+ - *show-ccache-stats
+ - *save-cache
+
+# ------------------------------------------------------------------------------
+ macos-debug:
+ macos:
+ xcode: "9.0"
+ environment:
+ BUILDTYPE: Debug
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ steps:
+ - checkout
+ - *install-macos-dependencies
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-macos-test
+ - *check-public-symbols
+ - run:
+ name: Lint plist files
+ command: make macos-lint
+ - *show-ccache-stats
+ - *save-cache
+ - store_artifacts:
+ path: test/fixtures
+ destination: test/fixtures
+
+# ------------------------------------------------------------------------------
+ macos-debug-qt5:
+ macos:
+ xcode: "9.0"
+ environment:
+ BUILDTYPE: Debug
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ steps:
+ - checkout
+ - *install-macos-dependencies
+ - *install-macos-qt-dependencies
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-qt-app
+ - *build-qt-test
+ - run:
+ name: Run qt-test
+ command: make run-qt-test
+ - *show-ccache-stats
+ - *save-cache
+ - store_artifacts:
+ path: test/fixtures
+ destination: test/fixtures
+
+# ------------------------------------------------------------------------------
+ macos-release-node4:
+ macos:
+ xcode: "9.0"
+ environment:
+ BUILDTYPE: RelWithDebInfo
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ steps:
+ - checkout
+ - *install-macos-dependencies
+ - *install-macos-node4-dependencies
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-node
+ - *show-ccache-stats
+ - *save-cache
+ - *run-node-macos-tests
+ - *publish-node-package
+ - *upload-render-tests
+
+# ------------------------------------------------------------------------------
+ macos-release-node6:
+ macos:
+ xcode: "9.0"
+ environment:
+ BUILDTYPE: RelWithDebInfo
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ steps:
+ - checkout
+ - *install-macos-dependencies
+ - *install-macos-node6-dependencies
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - *build-node
+ - *show-ccache-stats
+ - *save-cache
+ - *run-node-macos-tests
+ - *publish-node-package
+ - *upload-render-tests
diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake
index 3736df11f6..9161209128 100644
--- a/cmake/benchmark-files.cmake
+++ b/cmake/benchmark-files.cmake
@@ -3,12 +3,19 @@
set(MBGL_BENCHMARK_FILES
# api
benchmark/api/query.benchmark.cpp
+ benchmark/api/render.benchmark.cpp
+
+ # 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
@@ -16,6 +23,8 @@ set(MBGL_BENCHMARK_FILES
# src/mbgl/benchmark
benchmark/src/mbgl/benchmark/benchmark.cpp
- benchmark/src/mbgl/benchmark/util.cpp
- benchmark/src/mbgl/benchmark/util.hpp
+ benchmark/src/mbgl/benchmark/stub_geometry_tile_feature.hpp
+
+ # util
+ benchmark/util/dtoa.benchmark.cpp
)
diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake
index f4c59fa01e..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
@@ -17,7 +13,9 @@ target_link_libraries(mbgl-benchmark
PRIVATE mbgl-core
)
+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)
@@ -25,3 +23,12 @@ target_add_mason_package(mbgl-benchmark PRIVATE vector-tile)
mbgl_platform_benchmark()
create_source_groups(mbgl-benchmark)
+
+initialize_xcode_cxx_build_settings(mbgl-benchmark)
+
+xcode_create_scheme(
+ TARGET mbgl-benchmark
+ OPTIONAL_ARGS
+ "--benchmark_filter=Category.*"
+ "--benchmark_repetitions=1"
+)
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index fc476fcd15..15812da42d 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -8,6 +8,7 @@ set(MBGL_CORE_FILES
include/mbgl/actor/message.hpp
include/mbgl/actor/scheduler.hpp
src/mbgl/actor/mailbox.cpp
+ src/mbgl/actor/scheduler.cpp
# algorithm
src/mbgl/algorithm/covered_by_children.hpp
@@ -15,6 +16,7 @@ set(MBGL_CORE_FILES
src/mbgl/algorithm/generate_clip_ids.hpp
src/mbgl/algorithm/generate_clip_ids_impl.hpp
src/mbgl/algorithm/update_renderables.hpp
+ src/mbgl/algorithm/update_tile_masks.hpp
# annotation
include/mbgl/annotation/annotation.hpp
@@ -73,8 +75,6 @@ set(MBGL_CORE_FILES
src/mbgl/gl/program.hpp
src/mbgl/gl/program_binary_extension.hpp
src/mbgl/gl/renderbuffer.hpp
- src/mbgl/gl/segment.cpp
- src/mbgl/gl/segment.hpp
src/mbgl/gl/state.hpp
src/mbgl/gl/stencil_mode.cpp
src/mbgl/gl/stencil_mode.hpp
@@ -84,6 +84,8 @@ set(MBGL_CORE_FILES
src/mbgl/gl/uniform.hpp
src/mbgl/gl/value.cpp
src/mbgl/gl/value.hpp
+ src/mbgl/gl/vertex_array.cpp
+ src/mbgl/gl/vertex_array.hpp
src/mbgl/gl/vertex_array_extension.hpp
src/mbgl/gl/vertex_buffer.hpp
@@ -97,25 +99,20 @@ set(MBGL_CORE_FILES
src/mbgl/layout/symbol_instance.hpp
src/mbgl/layout/symbol_layout.cpp
src/mbgl/layout/symbol_layout.hpp
+ src/mbgl/layout/symbol_projection.cpp
+ src/mbgl/layout/symbol_projection.hpp
# map
- include/mbgl/map/backend.hpp
- include/mbgl/map/backend_scope.hpp
include/mbgl/map/camera.hpp
include/mbgl/map/change.hpp
include/mbgl/map/map.hpp
include/mbgl/map/map_observer.hpp
include/mbgl/map/mode.hpp
- include/mbgl/map/query.hpp
- include/mbgl/map/view.hpp
- src/mbgl/map/backend.cpp
- src/mbgl/map/backend_scope.cpp
src/mbgl/map/map.cpp
src/mbgl/map/transform.cpp
src/mbgl/map/transform.hpp
src/mbgl/map/transform_state.cpp
src/mbgl/map/transform_state.hpp
- src/mbgl/map/update.hpp
src/mbgl/map/zoom_history.hpp
# math
@@ -152,34 +149,38 @@ set(MBGL_CORE_FILES
src/mbgl/programs/programs.hpp
src/mbgl/programs/raster_program.cpp
src/mbgl/programs/raster_program.hpp
+ src/mbgl/programs/segment.hpp
src/mbgl/programs/symbol_program.cpp
src/mbgl/programs/symbol_program.hpp
src/mbgl/programs/uniforms.hpp
# 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
+ src/mbgl/renderer/backend_scope.cpp
src/mbgl/renderer/bucket.hpp
src/mbgl/renderer/bucket_parameters.cpp
src/mbgl/renderer/bucket_parameters.hpp
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
src/mbgl/renderer/image_atlas.hpp
src/mbgl/renderer/image_manager.cpp
src/mbgl/renderer/image_manager.hpp
+ src/mbgl/renderer/paint_parameters.cpp
src/mbgl/renderer/paint_parameters.hpp
src/mbgl/renderer/paint_property_binder.hpp
src/mbgl/renderer/paint_property_statistics.hpp
- src/mbgl/renderer/painter.cpp
- src/mbgl/renderer/painter.hpp
src/mbgl/renderer/possibly_evaluated_property_value.hpp
src/mbgl/renderer/property_evaluation_parameters.hpp
src/mbgl/renderer/property_evaluator.hpp
- src/mbgl/renderer/render_item.hpp
src/mbgl/renderer/render_layer.cpp
src/mbgl/renderer/render_layer.hpp
src/mbgl/renderer/render_light.cpp
@@ -188,13 +189,18 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/render_source.cpp
src/mbgl/renderer/render_source.hpp
src/mbgl/renderer/render_source_observer.hpp
- src/mbgl/renderer/render_style.cpp
- src/mbgl/renderer/render_style.hpp
- src/mbgl/renderer/render_style_observer.hpp
+ src/mbgl/renderer/render_static_data.cpp
+ src/mbgl/renderer/render_static_data.hpp
src/mbgl/renderer/render_tile.cpp
src/mbgl/renderer/render_tile.hpp
+ src/mbgl/renderer/renderer.cpp
+ 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
src/mbgl/renderer/tile_parameters.hpp
src/mbgl/renderer/tile_pyramid.cpp
src/mbgl/renderer/tile_pyramid.hpp
@@ -235,18 +241,9 @@ set(MBGL_CORE_FILES
src/mbgl/renderer/layers/render_symbol_layer.cpp
src/mbgl/renderer/layers/render_symbol_layer.hpp
- # renderer/painters
- src/mbgl/renderer/painters/painter_background.cpp
- src/mbgl/renderer/painters/painter_circle.cpp
- src/mbgl/renderer/painters/painter_clipping.cpp
- src/mbgl/renderer/painters/painter_debug.cpp
- src/mbgl/renderer/painters/painter_fill.cpp
- src/mbgl/renderer/painters/painter_fill_extrusion.cpp
- src/mbgl/renderer/painters/painter_line.cpp
- src/mbgl/renderer/painters/painter_raster.cpp
- src/mbgl/renderer/painters/painter_symbol.cpp
-
# 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
@@ -261,6 +258,8 @@ set(MBGL_CORE_FILES
src/mbgl/shaders/circle.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
@@ -313,8 +312,6 @@ set(MBGL_CORE_FILES
include/mbgl/storage/resource_transform.hpp
include/mbgl/storage/response.hpp
src/mbgl/storage/asset_file_source.hpp
- src/mbgl/storage/file_source_request.cpp
- src/mbgl/storage/file_source_request.hpp
src/mbgl/storage/http_file_source.hpp
src/mbgl/storage/local_file_source.hpp
src/mbgl/storage/network_status.cpp
@@ -339,6 +336,8 @@ set(MBGL_CORE_FILES
include/mbgl/style/types.hpp
include/mbgl/style/undefined.hpp
src/mbgl/style/collection.hpp
+ src/mbgl/style/custom_tile_loader.cpp
+ src/mbgl/style/custom_tile_loader.hpp
src/mbgl/style/image.cpp
src/mbgl/style/image_impl.cpp
src/mbgl/style/image_impl.hpp
@@ -369,23 +368,83 @@ set(MBGL_CORE_FILES
# style/conversion
include/mbgl/style/conversion/constant.hpp
include/mbgl/style/conversion/coordinate.hpp
+ include/mbgl/style/conversion/custom_geometry_source_options.hpp
include/mbgl/style/conversion/data_driven_property_value.hpp
+ include/mbgl/style/conversion/expression.hpp
include/mbgl/style/conversion/filter.hpp
include/mbgl/style/conversion/function.hpp
include/mbgl/style/conversion/geojson.hpp
include/mbgl/style/conversion/geojson_options.hpp
+ include/mbgl/style/conversion/get_json_type.hpp
include/mbgl/style/conversion/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/expression.hpp
+ include/mbgl/style/expression/find_zoom_curve.hpp
+ include/mbgl/style/expression/get_covering_stops.hpp
+ include/mbgl/style/expression/interpolate.hpp
+ include/mbgl/style/expression/is_constant.hpp
+ include/mbgl/style/expression/is_expression.hpp
+ include/mbgl/style/expression/let.hpp
+ include/mbgl/style/expression/literal.hpp
+ include/mbgl/style/expression/match.hpp
+ include/mbgl/style/expression/parsing_context.hpp
+ include/mbgl/style/expression/step.hpp
+ include/mbgl/style/expression/type.hpp
+ include/mbgl/style/expression/value.hpp
+ src/mbgl/style/expression/array_assertion.cpp
+ src/mbgl/style/expression/assertion.cpp
+ src/mbgl/style/expression/at.cpp
+ src/mbgl/style/expression/boolean_operator.cpp
+ src/mbgl/style/expression/case.cpp
+ src/mbgl/style/expression/check_subtype.cpp
+ src/mbgl/style/expression/coalesce.cpp
+ src/mbgl/style/expression/coercion.cpp
+ src/mbgl/style/expression/compound_expression.cpp
+ src/mbgl/style/expression/find_zoom_curve.cpp
+ src/mbgl/style/expression/get_covering_stops.cpp
+ src/mbgl/style/expression/interpolate.cpp
+ src/mbgl/style/expression/is_constant.cpp
+ src/mbgl/style/expression/is_expression.cpp
+ src/mbgl/style/expression/let.cpp
+ src/mbgl/style/expression/literal.cpp
+ src/mbgl/style/expression/match.cpp
+ src/mbgl/style/expression/parsing_context.cpp
+ src/mbgl/style/expression/step.cpp
+ src/mbgl/style/expression/util.cpp
+ src/mbgl/style/expression/util.hpp
+ src/mbgl/style/expression/value.cpp
# style/function
include/mbgl/style/function/camera_function.hpp
@@ -394,11 +453,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
@@ -450,10 +511,14 @@ 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_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
@@ -473,8 +538,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
@@ -487,15 +554,22 @@ set(MBGL_CORE_FILES
src/mbgl/text/glyph_pbf.cpp
src/mbgl/text/glyph_pbf.hpp
src/mbgl/text/glyph_range.hpp
- src/mbgl/text/placement_config.hpp
+ src/mbgl/text/local_glyph_rasterizer.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
src/mbgl/text/shaping.hpp
# 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
src/mbgl/tile/geometry_tile.cpp
src/mbgl/tile/geometry_tile.hpp
src/mbgl/tile/geometry_tile_data.cpp
@@ -510,7 +584,7 @@ set(MBGL_CORE_FILES
src/mbgl/tile/tile.hpp
src/mbgl/tile/tile_cache.cpp
src/mbgl/tile/tile_cache.hpp
- src/mbgl/tile/tile_id.hpp
+ src/mbgl/tile/tile_id_hash.cpp
src/mbgl/tile/tile_id_io.cpp
src/mbgl/tile/tile_loader.hpp
src/mbgl/tile/tile_loader_impl.hpp
@@ -521,7 +595,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
@@ -553,10 +626,12 @@ set(MBGL_CORE_FILES
include/mbgl/util/run_loop.hpp
include/mbgl/util/size.hpp
include/mbgl/util/string.hpp
+ include/mbgl/util/thread.hpp
include/mbgl/util/tileset.hpp
include/mbgl/util/timer.hpp
include/mbgl/util/traits.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
@@ -575,7 +650,7 @@ set(MBGL_CORE_FILES
src/mbgl/util/event.cpp
src/mbgl/util/font_stack.cpp
src/mbgl/util/geo.cpp
- src/mbgl/util/geojson.cpp
+ src/mbgl/util/geojson_impl.cpp
src/mbgl/util/grid_index.cpp
src/mbgl/util/grid_index.hpp
src/mbgl/util/http_header.cpp
@@ -610,12 +685,13 @@ set(MBGL_CORE_FILES
src/mbgl/util/stopwatch.hpp
src/mbgl/util/string.cpp
src/mbgl/util/thread_local.hpp
- src/mbgl/util/thread.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/tiny_sdf.cpp
+ src/mbgl/util/tiny_sdf.hpp
src/mbgl/util/token.hpp
src/mbgl/util/url.cpp
src/mbgl/util/url.hpp
diff --git a/cmake/core.cmake b/cmake/core.cmake
index c4e4d2abc7..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)
@@ -32,3 +26,7 @@ target_add_mason_package(mbgl-core PRIVATE vector-tile)
mbgl_platform_core()
create_source_groups(mbgl-core)
+
+xcode_create_scheme(TARGET mbgl-core)
+
+initialize_xcode_cxx_build_settings(mbgl-core)
diff --git a/platform/macos/scripts/executable.xcscheme b/cmake/executable.xcscheme
index c6a8d04d30..44146a621d 100644
--- a/platform/macos/scripts/executable.xcscheme
+++ b/cmake/executable.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0810"
+ LastUpgradeVersion = "0830"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -14,10 +14,10 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@@ -32,10 +32,10 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
@@ -47,7 +47,7 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "YES"
- customWorkingDirectory = "{{WORKING_DIRECTORY}}"
+ customWorkingDirectory = "${XCSCHEME_WORKING_DIRECTORY}"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
@@ -56,22 +56,18 @@
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</BuildableProductRunnable>
- <CommandLineArguments>
- <CommandLineArgument
- argument = "--gtest_filter="
- isEnabled = "NO">
- </CommandLineArgument>
+ <CommandLineArguments>${XCSCHEME_COMMAND_LINE_ARGS}
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "MAPBOX_ACCESS_TOKEN"
- value = "{{MAPBOX_ACCESS_TOKEN}}"
+ value = "$ENV{MAPBOX_ACCESS_TOKEN}"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
@@ -83,16 +79,16 @@
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "YES"
- customWorkingDirectory = "{{WORKING_DIRECTORY}}"
+ customWorkingDirectory = "${XCSCHEME_WORKING_DIRECTORY}"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
diff --git a/cmake/filesource.cmake b/cmake/filesource.cmake
new file mode 100644
index 0000000000..861a845d6e
--- /dev/null
+++ b/cmake/filesource.cmake
@@ -0,0 +1,49 @@
+add_library(mbgl-filesource STATIC
+ # File source
+ include/mbgl/storage/default_file_source.hpp
+ platform/default/default_file_source.cpp
+ platform/default/mbgl/storage/file_source_request.hpp
+ platform/default/file_source_request.cpp
+ include/mbgl/storage/online_file_source.hpp
+ platform/default/online_file_source.cpp
+ src/mbgl/storage/http_file_source.hpp
+ src/mbgl/storage/asset_file_source.hpp
+ platform/default/asset_file_source.cpp
+ src/mbgl/storage/local_file_source.hpp
+ platform/default/local_file_source.cpp
+
+ # Offline
+ include/mbgl/storage/offline.hpp
+ platform/default/mbgl/storage/offline.cpp
+ platform/default/mbgl/storage/offline_database.hpp
+ platform/default/mbgl/storage/offline_database.cpp
+ platform/default/mbgl/storage/offline_download.hpp
+ platform/default/mbgl/storage/offline_download.cpp
+
+ # Database
+ platform/default/sqlite3.hpp
+)
+
+target_add_mason_package(mbgl-filesource PUBLIC geometry)
+target_add_mason_package(mbgl-filesource PUBLIC variant)
+target_add_mason_package(mbgl-filesource PRIVATE rapidjson)
+target_add_mason_package(mbgl-filesource PRIVATE boost)
+target_add_mason_package(mbgl-filesource PRIVATE geojson)
+
+target_include_directories(mbgl-filesource
+ PRIVATE include
+ PRIVATE src
+ PRIVATE platform/default
+)
+
+target_link_libraries(mbgl-filesource
+ PUBLIC mbgl-core
+)
+
+mbgl_filesource()
+
+create_source_groups(mbgl-filesource)
+
+xcode_create_scheme(TARGET mbgl-filesource)
+
+initialize_xcode_cxx_build_settings(mbgl-filesource)
diff --git a/cmake/glfw.cmake b/cmake/glfw.cmake
index 744477e39a..2b2cb47ecb 100644
--- a/cmake/glfw.cmake
+++ b/cmake/glfw.cmake
@@ -5,25 +5,11 @@ add_executable(mbgl-glfw
target_sources(mbgl-glfw
PRIVATE platform/glfw/glfw_view.hpp
PRIVATE platform/glfw/glfw_view.cpp
+ PRIVATE platform/glfw/glfw_renderer_frontend.hpp
+ PRIVATE platform/glfw/glfw_renderer_frontend.cpp
PRIVATE platform/glfw/settings_json.hpp
PRIVATE platform/glfw/settings_json.cpp
PRIVATE platform/default/mbgl/util/default_styles.hpp
- PRIVATE platform/default/mbgl/util/default_styles.cpp
-)
-
-# Our GL implementation is internal to mbgl-core, which causes the GL header to
-# be included after GLFW's own header. They both attempt to define GLAPIENTRY,
-# but unfortunately the GL header doesn't check if it was previously defined,
-# causing a macro redefinition compiler error.
-# There is no particular compiler warning flag to ignore this check on GCC
-# neither it does accept ignoring '-Werror' via diagnostics pragmas. We can
-# only suppress this by either replacing the header path inclusion from -I to
-# -isystem, or completely suppressing errors. Until the former solution is not
-# available, we'll suppress the errors from that definition file.
-set_source_files_properties(platform/glfw/glfw_view.cpp PROPERTIES COMPILE_FLAGS -Wno-error)
-
-target_compile_options(mbgl-glfw
- PRIVATE -fvisibility-inlines-hidden
)
target_include_directories(mbgl-glfw
@@ -39,7 +25,23 @@ 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()
create_source_groups(mbgl-glfw)
+
+initialize_xcode_cxx_build_settings(mbgl-glfw)
+
+xcode_create_scheme(
+ TARGET mbgl-glfw
+ OPTIONAL_ARGS
+ "--style=file.json"
+ "--lon=0"
+ "--lat=0"
+ "--zoom=1"
+ "--bearing=0"
+ "--pitch=0"
+ "--fullscreen"
+ "--benchmark"
+)
diff --git a/platform/macos/scripts/library.xcscheme b/cmake/library.xcscheme
index 5472d3c821..320a2f851f 100644
--- a/platform/macos/scripts/library.xcscheme
+++ b/cmake/library.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0810"
+ LastUpgradeVersion = "0830"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -14,10 +14,10 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@@ -45,10 +45,10 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
@@ -63,10 +63,10 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
diff --git a/cmake/loop-darwin.cmake b/cmake/loop-darwin.cmake
index 92ea631809..692aecb8a2 100644
--- a/cmake/loop-darwin.cmake
+++ b/cmake/loop-darwin.cmake
@@ -4,16 +4,11 @@ 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
)
create_source_groups(mbgl-loop-darwin)
+
+xcode_create_scheme(TARGET mbgl-loop-darwin) \ No newline at end of file
diff --git a/cmake/loop-uv.cmake b/cmake/loop-uv.cmake
index 182b0d6f90..f4e7ced00e 100644
--- a/cmake/loop-uv.cmake
+++ b/cmake/loop-uv.cmake
@@ -4,11 +4,6 @@ add_library(mbgl-loop-uv STATIC
platform/default/timer.cpp
)
-target_compile_options(mbgl-loop-uv
- PRIVATE -fPIC
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-loop-uv
PRIVATE include
PRIVATE src
@@ -19,3 +14,5 @@ target_link_libraries(mbgl-loop-uv
)
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 9ef2ddd306..0393ff366e 100644
--- a/cmake/mbgl.cmake
+++ b/cmake/mbgl.cmake
@@ -95,6 +95,16 @@ function(_get_xcconfig_property target var)
get_property(result TARGET ${target} PROPERTY INTERFACE_${var} SET)
if (result)
get_property(result TARGET ${target} PROPERTY INTERFACE_${var})
+ if (var STREQUAL "LINK_LIBRARIES")
+ # Remove target names from the list of linker flags, since Xcode can't deal with them.
+ set(link_flags)
+ foreach(item IN LISTS result)
+ if (NOT TARGET ${item})
+ list(APPEND link_flags ${item})
+ endif()
+ endforeach()
+ set(result "${link_flags}")
+ endif()
string(REPLACE ";-framework " ";-framework;" result "${result}")
string(REPLACE ";" "\" \"" result "${result}")
string(REPLACE "-" "_" target "${target}")
@@ -114,6 +124,35 @@ function(write_xcconfig_target_properties)
)
endfunction()
+# Set Xcode project build settings to be consistent with the CXX flags we're
+# using. (Otherwise, Xcode's defaults may override some of these.)
+macro(initialize_xcode_cxx_build_settings target)
+ # -Wall
+ set_xcode_property(${target} GCC_WARN_SIGN_COMPARE YES)
+ set_xcode_property(${target} GCC_WARN_UNINITIALIZED_AUTOS YES)
+ set_xcode_property(${target} GCC_WARN_UNKNOWN_PRAGMAS YES)
+ set_xcode_property(${target} GCC_WARN_UNUSED_FUNCTION YES)
+ set_xcode_property(${target} GCC_WARN_UNUSED_LABEL YES)
+ set_xcode_property(${target} GCC_WARN_UNUSED_PARAMETER YES)
+ set_xcode_property(${target} GCC_WARN_UNUSED_VARIABLE YES)
+
+ # -Wextra
+ set_xcode_property(${target} CLANG_WARN_EMPTY_BODY YES)
+ set_xcode_property(${target} GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS YES)
+
+ # -Wshadow
+ set_xcode_property(${target} GCC_WARN_SHADOW 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.
set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14")
set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14")
diff --git a/cmake/node.cmake b/cmake/node.cmake
index 6833cb983f..0c2f556b7d 100644
--- a/cmake/node.cmake
+++ b/cmake/node.cmake
@@ -13,6 +13,7 @@ set_target_properties("mbgl-node" PROPERTIES CXX_STANDARD 14)
target_sources(mbgl-node
PRIVATE platform/node/src/node_logging.hpp
PRIVATE platform/node/src/node_logging.cpp
+ PRIVATE platform/node/src/node_conversion.hpp
PRIVATE platform/node/src/node_map.hpp
PRIVATE platform/node/src/node_map.cpp
PRIVATE platform/node/src/node_request.hpp
@@ -21,14 +22,11 @@ target_sources(mbgl-node
PRIVATE platform/node/src/node_feature.cpp
PRIVATE platform/node/src/node_thread_pool.hpp
PRIVATE platform/node/src/node_thread_pool.cpp
+ PRIVATE platform/node/src/node_expression.hpp
+ PRIVATE platform/node/src/node_expression.cpp
PRIVATE platform/node/src/util/async_queue.hpp
)
-target_compile_options(mbgl-node
- PRIVATE -fPIC
- PRIVATE -fvisibility-inlines-hidden
-)
-
target_include_directories(mbgl-node
PRIVATE platform/default
)
@@ -53,3 +51,61 @@ add_custom_command(
mbgl_platform_node()
create_source_groups(mbgl-node)
+
+initialize_xcode_cxx_build_settings(mbgl-node)
+
+xcode_create_scheme(
+ TARGET mbgl-node
+)
+
+xcode_create_scheme(
+ TARGET mbgl-node
+ TYPE node
+ NAME "node tests"
+ ARGS
+ "node_modules/.bin/tape platform/node/test/js/**/*.test.js"
+)
+
+xcode_create_scheme(
+ TARGET mbgl-node
+ TYPE node
+ NAME "node render tests"
+ ARGS
+ "platform/node/test/render.test.js"
+ OPTIONAL_ARGS
+ "group"
+ "test"
+)
+
+xcode_create_scheme(
+ TARGET mbgl-node
+ TYPE node
+ NAME "node query tests"
+ ARGS
+ "platform/node/test/query.test.js"
+ OPTIONAL_ARGS
+ "group"
+ "test"
+)
+
+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"
+ OPTIONAL_ARGS
+ "group"
+ "test"
+)
diff --git a/platform/macos/scripts/node.xcscheme b/cmake/node.xcscheme
index 6f541deca3..0daffa46e6 100644
--- a/platform/macos/scripts/node.xcscheme
+++ b/cmake/node.xcscheme
@@ -14,10 +14,10 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@@ -38,34 +38,30 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "YES"
- customWorkingDirectory = "{{WORKING_DIRECTORY}}"
+ customWorkingDirectory = "${XCSCHEME_WORKING_DIRECTORY}"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<PathRunnable
runnableDebuggingMode = "0"
- FilePath = "{{NODE_PATH}}/node">
+ FilePath = "${XCSCHEME_NODE_EXECUTABLE}">
</PathRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</MacroExpansion>
- <CommandLineArguments>
- <CommandLineArgument
- argument = "{{NODE_ARGUMENT}}"
- isEnabled = "YES">
- </CommandLineArgument>
+ <CommandLineArguments>${XCSCHEME_COMMAND_LINE_ARGS}
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "PATH"
- value = "{{NODE_PATH}}:$PATH"
+ value = "${XCSCHEME_NODE_PATH}:$PATH"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
@@ -77,15 +73,15 @@
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "YES"
- customWorkingDirectory = "{{WORKING_DIRECTORY}}"
+ customWorkingDirectory = "${XCSCHEME_WORKING_DIRECTORY}"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "{{BLUEPRINT_ID}}"
- BuildableName = "{{BUILDABLE_NAME}}"
- BlueprintName = "{{BLUEPRINT_NAME}}"
- ReferencedContainer = "container:{{CONTAINER}}">
+ BlueprintIdentifier = "${XCSCHEME_BLUEPRINT_ID}"
+ BuildableName = "${XCSCHEME_BUILDABLE_NAME}"
+ BlueprintName = "${XCSCHEME_BLUEPRINT_NAME}"
+ ReferencedContainer = "container:${XCSCHEME_CONTAINER}">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
diff --git a/cmake/offline.cmake b/cmake/offline.cmake
index d0124e661f..13b6c3fe70 100644
--- a/cmake/offline.cmake
+++ b/cmake/offline.cmake
@@ -4,11 +4,6 @@ add_executable(mbgl-offline
target_sources(mbgl-offline
PRIVATE platform/default/mbgl/util/default_styles.hpp
- PRIVATE platform/default/mbgl/util/default_styles.cpp
-)
-
-target_compile_options(mbgl-offline
- PRIVATE -fvisibility-inlines-hidden
)
target_include_directories(mbgl-offline
@@ -20,8 +15,23 @@ 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()
create_source_groups(mbgl-offline)
+
+xcode_create_scheme(
+ TARGET mbgl-offline
+ OPTIONAL_ARGS
+ "--style=file.json"
+ "--north=37.2"
+ "--west=-122.8"
+ "--south=38.1"
+ "--east=-121.7"
+ "--minZoom=0.0"
+ "--maxZoom=15.0"
+ "--pixelRatio=1.0"
+ "--token="
+ "--output=offline.db"
+)
diff --git a/cmake/render.cmake b/cmake/render.cmake
index 023b3c21e3..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,8 +11,30 @@ 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()
create_source_groups(mbgl-render)
+
+initialize_xcode_cxx_build_settings(mbgl-render)
+
+xcode_create_scheme(
+ TARGET mbgl-render
+ OPTIONAL_ARGS
+ "--style=file.json"
+ "--lon=0"
+ "--lat=0"
+ "--zoom=0"
+ "--bearing=0"
+ "--pitch=0"
+ "--width=512"
+ "--height=512"
+ "--ratio=1"
+ "--token="
+ "--debug"
+ "--output=out.png"
+ "--cache=cache.sqlite"
+ "--assets=."
+)
diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake
index edbf100fe2..55420ff2a8 100644
--- a/cmake/test-files.cmake
+++ b/cmake/test-files.cmake
@@ -10,17 +10,20 @@ set(MBGL_TEST_FILES
test/algorithm/generate_clip_ids.test.cpp
test/algorithm/mock.hpp
test/algorithm/update_renderables.test.cpp
+ test/algorithm/update_tile_masks.test.cpp
# 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/render_missing.test.cpp
- test/api/repeated_render.test.cpp
+ test/api/recycle_map.cpp
+ test/api/zoom_history.cpp
# gl
test/gl/bucket.test.cpp
+ test/gl/context.test.cpp
test/gl/object.test.cpp
# include/mbgl
@@ -28,6 +31,7 @@ set(MBGL_TEST_FILES
# map
test/map/map.test.cpp
+ test/map/prefetch.test.cpp
test/map/transform.test.cpp
# math
@@ -40,6 +44,7 @@ set(MBGL_TEST_FILES
test/programs/symbol_program.test.cpp
# renderer
+ test/renderer/backend_scope.test.cpp
test/renderer/group_by_layout.test.cpp
test/renderer/image_manager.test.cpp
@@ -48,7 +53,6 @@ set(MBGL_TEST_FILES
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
@@ -85,6 +89,10 @@ set(MBGL_TEST_FILES
test/style/conversion/light.test.cpp
test/style/conversion/stringify.test.cpp
+ # style/expression
+ test/style/expression/expression.test.cpp
+ test/style/expression/util.test.cpp
+
# style
test/style/filter.test.cpp
@@ -104,12 +112,14 @@ set(MBGL_TEST_FILES
test/style/style_parser.test.cpp
# text
- test/text/glyph_loader.test.cpp
+ test/text/cross_tile_symbol_index.test.cpp
+ test/text/local_glyph_rasterizer.test.cpp
+ test/text/glyph_manager.test.cpp
test/text/glyph_pbf.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_tile.test.cpp
@@ -119,7 +129,9 @@ set(MBGL_TEST_FILES
# util
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
@@ -136,5 +148,6 @@ set(MBGL_TEST_FILES
test/util/tile_cover.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 8a5233f5a5..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)
@@ -40,3 +35,16 @@ target_add_mason_package(mbgl-test PRIVATE shelf-pack)
mbgl_platform_test()
create_source_groups(mbgl-test)
+
+initialize_xcode_cxx_build_settings(mbgl-test)
+
+xcode_create_scheme(
+ TARGET mbgl-test
+ OPTIONAL_ARGS
+ "--gtest_filter=Category.*"
+ "--gtest_repeat=0"
+ "--gtest_shuffle=0"
+ "--gtest_break_on_failure=0"
+ "--gtest_throw_on_failure=0"
+ "--gtest_catch_exceptions=0"
+ )
diff --git a/cmake/xcode.cmake b/cmake/xcode.cmake
new file mode 100644
index 0000000000..3537009330
--- /dev/null
+++ b/cmake/xcode.cmake
@@ -0,0 +1,75 @@
+function(get_target_filename OUTPUT TARGET)
+ get_target_property(_TYPE "${TARGET}" TYPE)
+ get_target_property(_PREFIX "${TARGET}" PREFIX)
+ if(NOT _PREFIX AND NOT _PREFIX STREQUAL "")
+ set(_PREFIX "${CMAKE_${_TYPE}_PREFIX}")
+ endif()
+ get_target_property(_BASENAME "${TARGET}" OUTPUT_NAME)
+ if(NOT _BASENAME)
+ get_target_property(_BASENAME "${TARGET}" NAME)
+ endif()
+ get_target_property(_SUFFIX "${TARGET}" SUFFIX)
+ if(NOT _SUFFIX AND NOT _SUFFIX STREQUAL "")
+ set(_SUFFIX "${CMAKE_${_TYPE}_SUFFIX}")
+ endif()
+ set(${OUTPUT} "${_PREFIX}${_BASENAME}${_SUFFIX}" PARENT_SCOPE)
+endfunction()
+
+function(xcode_create_scheme)
+ if (NOT CMAKE_GENERATOR STREQUAL "Xcode")
+ return()
+ endif()
+
+ cmake_parse_arguments(XCSCHEME "" "TARGET;TYPE;NAME" "ARGS;OPTIONAL_ARGS" ${ARGN})
+
+ if(XCSCHEME_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "xcode_create_scheme() called with unrecognized arguments: ${XCSCHEME_UNPARSED_ARGUMENTS}")
+ endif()
+
+ if(NOT XCSCHEME_TARGET)
+ message(FATAL_ERROR "xcode_create_scheme() called without required argument TARGET")
+ endif()
+
+ if(NOT XCSCHEME_TYPE)
+ get_target_property(_TYPE "${XCSCHEME_TARGET}" TYPE)
+ if (_TYPE MATCHES "^.*_LIBRARY$")
+ set(XCSCHEME_TYPE "library")
+ elseif(_TYPE STREQUAL "EXECUTABLE")
+ set(XCSCHEME_TYPE "executable")
+ else()
+ message(FATAL_ERROR "xcode_create_scheme() could not determine type of ${XCSCHEME_TARGET}")
+ endif()
+ endif()
+
+ if(NOT XCSCHEME_NAME)
+ set(XCSCHEME_NAME "${XCSCHEME_TARGET}")
+ endif()
+
+ set(XCODEPROJ_PATH "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.xcodeproj")
+ set(XCSCHEME_OUTPUT_FILE "${XCODEPROJ_PATH}/xcshareddata/xcschemes/${XCSCHEME_NAME}.xcscheme")
+
+ # Prevent overwriting of the scheme file on every CMake rerun.
+ if (EXISTS "${XCSCHEME_OUTPUT_FILE}")
+ return()
+ endif()
+
+ file(RELATIVE_PATH XCSCHEME_CONTAINER "${CMAKE_SOURCE_DIR}" "${XCODEPROJ_PATH}")
+ string(RANDOM LENGTH 24 ALPHABET "0123456789ABCDEF" XCSCHEME_BLUEPRINT_ID)
+ get_target_filename(XCSCHEME_BUILDABLE_NAME "${XCSCHEME_TARGET}")
+ set(XCSCHEME_BLUEPRINT_NAME "${XCSCHEME_TARGET}")
+ set(XCSCHEME_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
+ set(XCSCHEME_NODE_EXECUTABLE "${NodeJS_EXECUTABLE}")
+ get_filename_component(XCSCHEME_NODE_PATH "${NodeJS_EXECUTABLE}" DIRECTORY)
+
+ set(XCSCHEME_COMMAND_LINE_ARGS "")
+ foreach(_ARG IN LISTS XCSCHEME_ARGS)
+ set(XCSCHEME_COMMAND_LINE_ARGS "${XCSCHEME_COMMAND_LINE_ARGS}\n <CommandLineArgument\n argument = \"${_ARG}\"\n isEnabled = \"YES\">\n </CommandLineArgument>")
+ endforeach()
+ foreach(_ARG IN LISTS XCSCHEME_OPTIONAL_ARGS)
+ set(XCSCHEME_COMMAND_LINE_ARGS "${XCSCHEME_COMMAND_LINE_ARGS}\n <CommandLineArgument\n argument = \"${_ARG}\"\n isEnabled = \"NO\">\n </CommandLineArgument>")
+ endforeach()
+
+ configure_file(
+ "${CMAKE_SOURCE_DIR}/cmake/${XCSCHEME_TYPE}.xcscheme"
+ "${XCSCHEME_OUTPUT_FILE}")
+endfunction()
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/actor/actor.hpp b/include/mbgl/actor/actor.hpp
index 810114c513..a0df19208e 100644
--- a/include/mbgl/actor/actor.hpp
+++ b/include/mbgl/actor/actor.hpp
@@ -6,6 +6,8 @@
#include <mbgl/util/noncopyable.hpp>
#include <memory>
+#include <future>
+#include <type_traits>
namespace mbgl {
@@ -46,10 +48,18 @@ namespace mbgl {
template <class Object>
class Actor : public util::noncopyable {
public:
- template <class... Args>
+
+ // Enabled for Objects with a constructor taking ActorRef<Object> as the first parameter
+ template <typename U = Object, class... Args, typename std::enable_if<std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
Actor(Scheduler& scheduler, Args&&... args_)
- : mailbox(std::make_shared<Mailbox>(scheduler)),
- object(self(), std::forward<Args>(args_)...) {
+ : mailbox(std::make_shared<Mailbox>(scheduler)),
+ object(self(), std::forward<Args>(args_)...) {
+ }
+
+ // Enabled for plain Objects
+ template<typename U = Object, class... Args, typename std::enable_if<!std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
+ Actor(Scheduler& scheduler, Args&& ... args_)
+ : mailbox(std::make_shared<Mailbox>(scheduler)), object(std::forward<Args>(args_)...) {
}
~Actor() {
@@ -61,6 +71,17 @@ public:
mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...));
}
+ template <typename Fn, class... Args>
+ auto ask(Fn fn, Args&&... args) {
+ // Result type is deduced from the function's return type
+ using ResultType = typename std::result_of<decltype(fn)(Object, Args...)>::type;
+
+ std::promise<ResultType> promise;
+ auto future = promise.get_future();
+ mailbox->push(actor::makeMessage(std::move(promise), object, fn, std::forward<Args>(args)...));
+ return future;
+ }
+
ActorRef<std::decay_t<Object>> self() {
return ActorRef<std::decay_t<Object>>(object, mailbox);
}
diff --git a/include/mbgl/actor/actor_ref.hpp b/include/mbgl/actor/actor_ref.hpp
index aeb5bb4507..958ee3777c 100644
--- a/include/mbgl/actor/actor_ref.hpp
+++ b/include/mbgl/actor/actor_ref.hpp
@@ -35,6 +35,27 @@ public:
}
}
+ template <typename Fn, class... Args>
+ auto ask(Fn fn, Args&&... args) {
+ // Result type is deduced from the function's return type
+ using ResultType = typename std::result_of<decltype(fn)(Object, Args...)>::type;
+
+ std::promise<ResultType> promise;
+ auto future = promise.get_future();
+
+ if (auto mailbox = weakMailbox.lock()) {
+ mailbox->push(
+ actor::makeMessage(
+ std::move(promise), *object, fn, std::forward<Args>(args)...
+ )
+ );
+ } else {
+ promise.set_exception(std::make_exception_ptr(std::runtime_error("Actor has gone away")));
+ }
+
+ return future;
+ }
+
private:
Object* object;
std::weak_ptr<Mailbox> weakMailbox;
diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp
index cf071d4933..0a20993352 100644
--- a/include/mbgl/actor/message.hpp
+++ b/include/mbgl/actor/message.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include <mbgl/util/optional.hpp>
+
+#include <future>
#include <utility>
namespace mbgl {
@@ -36,6 +39,57 @@ public:
ArgsTuple argsTuple;
};
+template <class ResultType, class Object, class MemberFn, class ArgsTuple>
+class AskMessageImpl : public Message {
+public:
+ AskMessageImpl(std::promise<ResultType> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
+ : object(object_),
+ memberFn(memberFn_),
+ argsTuple(std::move(argsTuple_)),
+ promise(std::move(promise_)) {
+ }
+
+ void operator()() override {
+ promise.set_value(ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>()));
+ }
+
+ template <std::size_t... I>
+ ResultType ask(std::index_sequence<I...>) {
+ return (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
+ }
+
+ Object& object;
+ MemberFn memberFn;
+ ArgsTuple argsTuple;
+ std::promise<ResultType> promise;
+};
+
+template <class Object, class MemberFn, class ArgsTuple>
+class AskMessageImpl<void, Object, MemberFn, ArgsTuple> : public Message {
+public:
+ AskMessageImpl(std::promise<void> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
+ : object(object_),
+ memberFn(memberFn_),
+ argsTuple(std::move(argsTuple_)),
+ promise(std::move(promise_)) {
+ }
+
+ void operator()() override {
+ ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>());
+ promise.set_value();
+ }
+
+ template <std::size_t... I>
+ void ask(std::index_sequence<I...>) {
+ (object.*memberFn)(std::move(std::get<I>(argsTuple))...);
+ }
+
+ Object& object;
+ MemberFn memberFn;
+ ArgsTuple argsTuple;
+ std::promise<void> promise;
+};
+
namespace actor {
template <class Object, class MemberFn, class... Args>
@@ -44,5 +98,11 @@ std::unique_ptr<Message> makeMessage(Object& object, MemberFn memberFn, Args&&..
return std::make_unique<MessageImpl<Object, MemberFn, decltype(tuple)>>(object, memberFn, std::move(tuple));
}
+template <class ResultType, class Object, class MemberFn, class... Args>
+std::unique_ptr<Message> makeMessage(std::promise<ResultType>&& promise, Object& object, MemberFn memberFn, Args&&... args) {
+ auto tuple = std::make_tuple(std::forward<Args>(args)...);
+ return std::make_unique<AskMessageImpl<ResultType, Object, MemberFn, decltype(tuple)>>(std::move(promise), object, memberFn, std::move(tuple));
+}
+
} // namespace actor
} // namespace mbgl
diff --git a/include/mbgl/actor/scheduler.hpp b/include/mbgl/actor/scheduler.hpp
index 83689c3348..d8a26ebeab 100644
--- a/include/mbgl/actor/scheduler.hpp
+++ b/include/mbgl/actor/scheduler.hpp
@@ -21,18 +21,21 @@ class Mailbox;
Subject to these constraints, processing can happen on whatever thread in the
pool is available.
- * `RunLoop` is a `Scheduler` that is typically used to create a mailbox and
- `ActorRef` for an object that lives on the main thread and is not itself wrapped
- as an `Actor`:
-
- auto mailbox = std::make_shared<Mailbox>(*util::RunLoop::Get());
+ * `Scheduler::GetCurrent()` is typically used to create a mailbox and `ActorRef`
+ for an object that lives on the main thread and is not itself wrapped an
+ `Actor`. The underlying implementation of this Scheduler should usually be
+ a `RunLoop`
+ auto mailbox = std::make_shared<Mailbox>(*Scheduler::Get());
Actor<Worker> worker(threadPool, ActorRef<Foo>(*this, mailbox));
*/
-
class Scheduler {
public:
virtual ~Scheduler() = default;
virtual void schedule(std::weak_ptr<Mailbox>) = 0;
+
+ // Set/Get the current Scheduler for this thread
+ static Scheduler* GetCurrent();
+ static void SetCurrent(Scheduler*);
};
} // namespace mbgl
diff --git a/include/mbgl/annotation/annotation.hpp b/include/mbgl/annotation/annotation.hpp
index 96e06ca222..bbe479b5ba 100644
--- a/include/mbgl/annotation/annotation.hpp
+++ b/include/mbgl/annotation/annotation.hpp
@@ -17,6 +17,10 @@ using AnnotationIDs = std::vector<AnnotationID>;
class SymbolAnnotation {
public:
+ SymbolAnnotation(Point<double> geometry_, std::string icon_ = {})
+ : geometry(std::move(geometry_)),
+ icon(std::move(icon_)) {}
+
Point<double> geometry;
std::string icon;
};
@@ -29,18 +33,36 @@ using ShapeAnnotationGeometry = variant<
class LineAnnotation {
public:
+ LineAnnotation(ShapeAnnotationGeometry geometry_,
+ style::DataDrivenPropertyValue<float> opacity_ = 1.0f,
+ style::DataDrivenPropertyValue<float> width_ = 1.0f,
+ style::DataDrivenPropertyValue<Color> color_ = Color::black())
+ : geometry(std::move(geometry_)),
+ opacity(std::move(opacity_)),
+ width(std::move(width_)),
+ color(std::move(color_)) {}
+
ShapeAnnotationGeometry geometry;
- style::DataDrivenPropertyValue<float> opacity { 1.0f };
- style::DataDrivenPropertyValue<float> width { 1.0f };
- style::DataDrivenPropertyValue<Color> color { Color::black() };
+ style::DataDrivenPropertyValue<float> opacity;
+ style::DataDrivenPropertyValue<float> width;
+ style::DataDrivenPropertyValue<Color> color;
};
class FillAnnotation {
public:
+ FillAnnotation(ShapeAnnotationGeometry geometry_,
+ style::DataDrivenPropertyValue<float> opacity_ = 1.0f,
+ style::DataDrivenPropertyValue<Color> color_ = Color::black(),
+ style::DataDrivenPropertyValue<Color> outlineColor_ = {})
+ : geometry(std::move(geometry_)),
+ opacity(std::move(opacity_)),
+ color(std::move(color_)),
+ outlineColor(std::move(outlineColor_)) {}
+
ShapeAnnotationGeometry geometry;
- style::DataDrivenPropertyValue<float> opacity { 1.0f };
- style::DataDrivenPropertyValue<Color> color { Color::black() };
- style::DataDrivenPropertyValue<Color> outlineColor {};
+ style::DataDrivenPropertyValue<float> opacity;
+ style::DataDrivenPropertyValue<Color> color;
+ style::DataDrivenPropertyValue<Color> outlineColor;
};
using Annotation = variant<
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index dd29d444bd..5ba23a76dd 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -4,13 +4,11 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/map/map_observer.hpp>
#include <mbgl/map/mode.hpp>
-#include <mbgl/util/geo.hpp>
-#include <mbgl/util/feature.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/size.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/map/camera.hpp>
-#include <mbgl/map/query.hpp>
+#include <mbgl/util/geometry.hpp>
#include <cstdint>
#include <string>
@@ -20,10 +18,9 @@
namespace mbgl {
-class Backend;
-class View;
class FileSource;
class Scheduler;
+class RendererFrontend;
namespace style {
class Image;
@@ -32,29 +29,26 @@ class Style;
class Map : private util::noncopyable {
public:
- explicit Map(Backend&,
+ explicit Map(RendererFrontend&,
+ MapObserver&,
Size size,
float pixelRatio,
FileSource&,
Scheduler&,
MapMode mapMode = MapMode::Continuous,
- GLContextMode contextMode = GLContextMode::Unique,
ConstrainMode constrainMode = ConstrainMode::HeightOnly,
- ViewportMode viewportMode = ViewportMode::Default,
- const optional<std::string>& programCacheDir = {});
+ ViewportMode viewportMode = ViewportMode::Default);
~Map();
// Register a callback that will get called (on the render thread) when all resources have
// been loaded and a complete render occurs.
using StillImageCallback = std::function<void (std::exception_ptr)>;
- void renderStill(View&, StillImageCallback callback);
+ void renderStill(StillImageCallback);
+ void renderStill(const CameraOptions&, MapDebugOptions, StillImageCallback);
// Triggers a repaint.
void triggerRepaint();
- // Main render function.
- void render(View&);
-
style::Style& getStyle();
const style::Style& getStyle() const;
@@ -73,6 +67,10 @@ public:
void jumpTo(const CameraOptions&);
void easeTo(const CameraOptions&, const AnimationOptions&);
void flyTo(const CameraOptions&, const AnimationOptions&);
+ CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&, optional<double> bearing = {}) const;
+ CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&, optional<double> bearing = {}) const;
+ CameraOptions cameraForGeometry(const Geometry<double>&, const EdgeInsets&, optional<double> bearing = {}) const;
+ LatLngBounds latLngBoundsForCamera(const CameraOptions&) const;
// Position
void moveBy(const ScreenCoordinate&, const AnimationOptions& = {});
@@ -89,9 +87,6 @@ public:
double getZoom() const;
void setLatLngZoom(const LatLng&, double zoom, const AnimationOptions& = {});
void setLatLngZoom(const LatLng&, double zoom, const EdgeInsets&, const AnimationOptions& = {});
- CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&) const;
- CameraOptions cameraForLatLngs(const std::vector<LatLng>&, const EdgeInsets&) const;
- LatLngBounds latLngBoundsForCamera(const CameraOptions&) const;
void resetZoom();
// Bounds
@@ -132,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;
@@ -149,16 +152,14 @@ public:
void updateAnnotation(AnnotationID, const Annotation&);
void removeAnnotation(AnnotationID);
- // Feature queries
- std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions& options = {});
- std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions& options = {});
- std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {});
-
- AnnotationIDs queryPointAnnotations(const ScreenBox&);
-
- // Memory
- void setSourceTileCacheSize(size_t);
- void onLowMemory();
+ // Tile prefetching
+ //
+ // When loading a map, if `PrefetchZoomDelta` is set to any number greater than 0, the map will
+ // first request a tile for `zoom = getZoom() - delta` in a attempt to display a full map at
+ // lower resolution as quick as possible. It will get clamped at the tile source minimum zoom.
+ // The default `delta` is 4.
+ void setPrefetchZoomDelta(uint8_t delta);
+ uint8_t getPrefetchZoomDelta() const;
// Debug
void setDebug(MapDebugOptions);
diff --git a/include/mbgl/map/mode.hpp b/include/mbgl/map/mode.hpp
index afec5c0a08..4ee289d855 100644
--- a/include/mbgl/map/mode.hpp
+++ b/include/mbgl/map/mode.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <mbgl/util/util.hpp>
#include <mbgl/util/traits.hpp>
#include <cstdint>
@@ -10,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
@@ -51,23 +44,23 @@ enum class MapDebugOptions : EnumType {
#endif // MBGL_USE_GLES2
};
-constexpr MapDebugOptions operator|(MapDebugOptions lhs, MapDebugOptions rhs) {
+MBGL_CONSTEXPR MapDebugOptions operator|(MapDebugOptions lhs, MapDebugOptions rhs) {
return MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs));
}
-constexpr MapDebugOptions& operator|=(MapDebugOptions& lhs, MapDebugOptions rhs) {
+MBGL_CONSTEXPR MapDebugOptions& operator|=(MapDebugOptions& lhs, MapDebugOptions rhs) {
return (lhs = MapDebugOptions(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs)));
}
-constexpr bool operator&(MapDebugOptions lhs, MapDebugOptions rhs) {
+MBGL_CONSTEXPR bool operator&(MapDebugOptions lhs, MapDebugOptions rhs) {
return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
}
-constexpr MapDebugOptions& operator&=(MapDebugOptions& lhs, MapDebugOptions rhs) {
+MBGL_CONSTEXPR MapDebugOptions& operator&=(MapDebugOptions& lhs, MapDebugOptions rhs) {
return (lhs = MapDebugOptions(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs)));
}
-constexpr MapDebugOptions operator~(MapDebugOptions value) {
+MBGL_CONSTEXPR MapDebugOptions operator~(MapDebugOptions value) {
return MapDebugOptions(~mbgl::underlying_type(value));
}
diff --git a/include/mbgl/map/view.hpp b/include/mbgl/map/view.hpp
deleted file mode 100644
index 295779fe51..0000000000
--- a/include/mbgl/map/view.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-namespace mbgl {
-
-class Map;
-
-class View {
-public:
- virtual ~View() = default;
-
- // Called when this View is used for rendering. Implementations should ensure that a renderable
- // object is bound and glClear/glDraw* calls can be done. They should also make sure that
- // calling .bind() repeatedly is a no-op and that the appropriate gl::Context values are
- // set to the current state.
- virtual void bind() = 0;
-};
-
-} // namespace mbgl
diff --git a/include/mbgl/math/log2.hpp b/include/mbgl/math/log2.hpp
index 8a3bc7f1c0..53d5e45545 100644
--- a/include/mbgl/math/log2.hpp
+++ b/include/mbgl/math/log2.hpp
@@ -17,9 +17,9 @@ typename std::enable_if_t<std::is_floating_point<T>::value, T> log2(T x)
// log2() is producing wrong results on ARMv5 binaries
// running on ARMv7+ CPUs.
#if defined(__ANDROID__)
- return std::log(x) / M_LN2;
+ return ::log(x) / M_LN2;
#else
- return std::log2(x);
+ return ::log2(x);
#endif
}
diff --git a/include/mbgl/map/backend_scope.hpp b/include/mbgl/renderer/backend_scope.hpp
index 4985cd197f..73bafc84c7 100644
--- a/include/mbgl/map/backend_scope.hpp
+++ b/include/mbgl/renderer/backend_scope.hpp
@@ -2,7 +2,7 @@
namespace mbgl {
-class Backend;
+class RendererBackend;
class BackendScope {
public:
@@ -15,17 +15,21 @@ public:
Explicit,
};
- BackendScope(Backend&, ScopeType = ScopeType::Explicit);
+ BackendScope(RendererBackend&, ScopeType = ScopeType::Explicit);
~BackendScope();
// Returns true when there is currently a BackendScope active in this thread.
static bool exists();
private:
+ void activate();
+ void deactivate();
+
BackendScope* priorScope;
BackendScope* nextScope;
- Backend& backend;
+ RendererBackend& backend;
const ScopeType scopeType;
+ bool activated = false;
};
} // namespace mbgl
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/map/query.hpp b/include/mbgl/renderer/query.hpp
index e114fa2ebd..4cadf4f017 100644
--- a/include/mbgl/map/query.hpp
+++ b/include/mbgl/renderer/query.hpp
@@ -13,6 +13,11 @@ namespace mbgl {
*/
class RenderedQueryOptions {
public:
+ RenderedQueryOptions(optional<std::vector<std::string>> layerIDs_ = {},
+ optional<style::Filter> filter_ = {})
+ : layerIDs(std::move(layerIDs_)),
+ filter(std::move(filter_)) {}
+
/** layerIDs to include in the query */
optional<std::vector<std::string>> layerIDs;
@@ -24,6 +29,11 @@ public:
*/
class SourceQueryOptions {
public:
+ SourceQueryOptions(optional<std::vector<std::string>> sourceLayers_ = {},
+ optional<style::Filter> filter_ = {})
+ : sourceLayers(std::move(sourceLayers_)),
+ filter(std::move(filter_)) {}
+
// Required for VectorSource, ignored for GeoJSONSource
optional<std::vector<std::string>> sourceLayers;
diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp
new file mode 100644
index 0000000000..db28ee92fc
--- /dev/null
+++ b/include/mbgl/renderer/renderer.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#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>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+class FileSource;
+class RendererBackend;
+class RendererObserver;
+class RenderedQueryOptions;
+class Scheduler;
+class SourceQueryOptions;
+class UpdateParameters;
+
+class Renderer {
+public:
+ Renderer(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&,
+ GLContextMode = GLContextMode::Unique,
+ const optional<std::string> programCacheDir = {},
+ const optional<std::string> localFontFamily = {});
+ ~Renderer();
+
+ void markContextLost();
+
+ void setObserver(RendererObserver*);
+
+ void render(const UpdateParameters&);
+
+ // Feature queries
+ std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions& options = {}) const;
+ std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options = {}) const;
+ std::vector<Feature> queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options = {}) const;
+ std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}) const;
+ AnnotationIDs queryPointAnnotations(const ScreenBox& box) const;
+ AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const;
+ AnnotationIDs getAnnotationIDs(const std::vector<Feature>&) const;
+
+ // Debug
+ void dumpDebugLogs();
+
+ // Memory
+ void onLowMemory();
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/map/backend.hpp b/include/mbgl/renderer/renderer_backend.hpp
index 2e73ad994c..b83c128169 100644
--- a/include/mbgl/map/backend.hpp
+++ b/include/mbgl/renderer/renderer_backend.hpp
@@ -1,8 +1,9 @@
#pragma once
-#include <mbgl/map/map_observer.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/size.hpp>
+#include <mbgl/util/util.hpp>
#include <memory>
#include <mutex>
@@ -15,12 +16,12 @@ using ProcAddress = void (*)();
using FramebufferID = uint32_t;
} // namespace gl
-class BackendScope;
-
-class Backend : public MapObserver {
+// The RendererBackend is used by the Renderer to facilitate
+// the actual rendering.
+class RendererBackend {
public:
- Backend();
- virtual ~Backend();
+ RendererBackend();
+ virtual ~RendererBackend();
// Returns the backend's context which manages OpenGL state.
gl::Context& getContext();
@@ -28,26 +29,28 @@ public:
// Called prior to rendering to update the internally assumed OpenGL state.
virtual void updateAssumedState() = 0;
- // Called when the map needs to be rendered; the backend should call Map::render() at some point
- // in the near future. (Not called for Map::renderStill() mode.)
- virtual void invalidate() = 0;
+ // Called when this backend is used for rendering. Implementations should ensure that a renderable
+ // object is bound and glClear/glDraw* calls can be done. They should also make sure that
+ // calling .bind() repeatedly is a no-op and that the appropriate gl::Context values are
+ // set to the current state.
+ virtual void bind() = 0;
+
+ virtual Size getFramebufferSize() const = 0;
protected:
- // Called with the name of an OpenGL extension that should be loaded. Backend implementations
+ // 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, in four situations:
+ // as a matched pair, exclusively through BackendScope, in two situations:
//
- // 1. When releasing GL resources during Map destruction
- // 2. When calling a CustomLayerInitializeFunction, during Map::addLayer
- // 3. When calling a CustomLayerDeinitializeFunction, during Map::removeLayer
- // 4. When rendering for Map::renderStill
- //
- // They are *not* called for Map::render; it is assumed that the correct context is already
- // activated prior to calling Map::render.
+ // 1. When releasing GL resources during Renderer destruction
+ // (Including calling CustomLayerDeinitializeFunction during RenderCustomLayer destruction)
+ // 2. When renderering through Renderer::render()
+ // (Including calling CustomLayerDeinitializeFunction for newly added custom layers and
+ // CustomLayerDeinitializeFunction on layer removal)
virtual void activate() = 0;
virtual void deactivate() = 0;
@@ -82,4 +85,8 @@ private:
friend class BackendScope;
};
+MBGL_CONSTEXPR bool operator==(const RendererBackend& a, const RendererBackend& b) {
+ return &a == &b;
+}
+
} // namespace mbgl
diff --git a/include/mbgl/renderer/renderer_frontend.hpp b/include/mbgl/renderer/renderer_frontend.hpp
new file mode 100644
index 0000000000..f72b0ccdde
--- /dev/null
+++ b/include/mbgl/renderer/renderer_frontend.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <memory>
+
+namespace mbgl {
+
+class RendererObserver;
+class UpdateParameters;
+
+// The RenderFrontend is the bridge between the Map and
+// platform used to update and observer the Renderer
+//
+// It hides any threading specifics and always replies on
+// the original thread.
+class RendererFrontend {
+public:
+
+ virtual ~RendererFrontend() = default;
+
+ // Must synchronously clean up the Renderer if set
+ virtual void reset() = 0;
+
+ // Implementer must bind the renderer observer to the renderer in a
+ // appropriate manner so that the callbacks occur on the main thread
+ virtual void setObserver(RendererObserver&) = 0;
+
+ // Coalescing updates is up to the implementer
+ virtual void update(std::shared_ptr<UpdateParameters>) = 0;
+};
+
+} // namespace mbgl
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index 9911e0ce67..b9c8de5052 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -34,7 +34,7 @@ public:
uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE);
~DefaultFileSource() override;
- bool supportsOptionalRequests() const override {
+ bool supportsCacheOnlyRequests() const override {
return true;
}
@@ -140,6 +140,7 @@ public:
void resume();
// For testing only.
+ void setOnlineStatus(bool);
void put(const Resource&, const Response&);
class Impl;
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
index 404c683fdb..0709a1c245 100644
--- a/include/mbgl/storage/file_source.hpp
+++ b/include/mbgl/storage/file_source.hpp
@@ -24,11 +24,11 @@ public:
// not be executed.
virtual std::unique_ptr<AsyncRequest> request(const Resource&, Callback) = 0;
- // When a file source supports optional requests, it must return true.
- // Optional requests are requests that aren't as urgent, but could be useful, e.g.
+ // When a file source supports consulting a local cache only, it must return true.
+ // Cache-only requests are requests that aren't as urgent, but could be useful, e.g.
// to cover part of the map while loading. The FileSource should only do cheap actions to
// retrieve the data, e.g. load it from a cache, but not from the internet.
- virtual bool supportsOptionalRequests() const {
+ virtual bool supportsCacheOnlyRequests() const {
return false;
}
};
diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp
index 818cfe2ba5..ef4a499e83 100644
--- a/include/mbgl/storage/offline.hpp
+++ b/include/mbgl/storage/offline.hpp
@@ -30,13 +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;
-
+ 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(style::SourceType, uint16_t tileSize, const Range<uint8_t>& zoomRange) const;
};
/*
diff --git a/include/mbgl/storage/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp
index ffd75662e6..28d70ce544 100644
--- a/include/mbgl/storage/online_file_source.hpp
+++ b/include/mbgl/storage/online_file_source.hpp
@@ -24,6 +24,9 @@ public:
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
+ // For testing only.
+ void setOnlineStatus(bool);
+
private:
friend class OnlineFileRequest;
diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp
index 7e9ced8049..318fa389f4 100644
--- a/include/mbgl/storage/resource.hpp
+++ b/include/mbgl/storage/resource.hpp
@@ -4,6 +4,8 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/util/font_stack.hpp>
#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/util.hpp>
+#include <mbgl/util/traits.hpp>
#include <string>
@@ -30,18 +32,28 @@ public:
int8_t z;
};
- enum Necessity : bool {
- Optional = false,
- Required = true,
+ enum class LoadingMethod : uint8_t {
+ None = 0b00,
+ Cache = 0b01,
+ Network = 0b10,
+
+ CacheOnly = Cache,
+ NetworkOnly = Network,
+ All = Cache | Network,
};
- Resource(Kind kind_, std::string url_, optional<TileData> tileData_ = {}, Necessity necessity_ = Required)
+ Resource(Kind kind_,
+ std::string url_,
+ optional<TileData> tileData_ = {},
+ LoadingMethod loadingMethod_ = LoadingMethod::All)
: kind(kind_),
- necessity(necessity_),
+ loadingMethod(loadingMethod_),
url(std::move(url_)),
tileData(std::move(tileData_)) {
}
+ bool hasLoadingMethod(LoadingMethod method);
+
static Resource style(const std::string& url);
static Resource source(const std::string& url);
static Resource tile(const std::string& urlTemplate,
@@ -50,7 +62,7 @@ public:
int32_t y,
int8_t z,
Tileset::Scheme scheme,
- Necessity = Required);
+ LoadingMethod = LoadingMethod::All);
static Resource glyphs(const std::string& urlTemplate,
const FontStack& fontStack,
const std::pair<uint16_t, uint16_t>& glyphRange);
@@ -59,7 +71,7 @@ public:
static Resource image(const std::string& url);
Kind kind;
- Necessity necessity;
+ LoadingMethod loadingMethod;
std::string url;
// Includes auxiliary data if this is a tile request.
@@ -68,6 +80,24 @@ public:
optional<Timestamp> priorModified = {};
optional<Timestamp> priorExpires = {};
optional<std::string> priorEtag = {};
+ std::shared_ptr<const std::string> priorData;
};
+
+MBGL_CONSTEXPR Resource::LoadingMethod operator|(Resource::LoadingMethod a, Resource::LoadingMethod b) {
+ return Resource::LoadingMethod(mbgl::underlying_type(a) | mbgl::underlying_type(b));
+}
+
+MBGL_CONSTEXPR Resource::LoadingMethod& operator|=(Resource::LoadingMethod& a, Resource::LoadingMethod b) {
+ return (a = a | b);
+}
+
+MBGL_CONSTEXPR Resource::LoadingMethod operator&(Resource::LoadingMethod a, Resource::LoadingMethod b) {
+ return Resource::LoadingMethod(mbgl::underlying_type(a) & mbgl::underlying_type(b));
+}
+
+inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) {
+ return (loadingMethod & method) != Resource::LoadingMethod::None;
+}
+
} // namespace mbgl
diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp
index 32fe4e0c8a..508400141b 100644
--- a/include/mbgl/storage/response.hpp
+++ b/include/mbgl/storage/response.hpp
@@ -2,7 +2,6 @@
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/optional.hpp>
-#include <mbgl/util/variant.hpp>
#include <string>
#include <memory>
@@ -27,6 +26,10 @@ public:
// This is set to true for 304 Not Modified responses.
bool notModified = false;
+ // This is set to true when the server requested that no expired resources be used by
+ // specifying "Cache-Control: must-revalidate".
+ bool mustRevalidate = false;
+
// The actual data of the response. Present only for non-error, non-notModified responses.
std::shared_ptr<const std::string> data;
@@ -37,6 +40,12 @@ public:
bool isFresh() const {
return expires ? *expires > util::now() : !error;
}
+
+ // Indicates whether we are allowed to use this response according to HTTP caching rules.
+ // It may or may not be stale.
+ bool isUsable() const {
+ return !mustRevalidate || (expires && *expires > util::now());
+ }
};
class Response::Error {
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..73b141e799
--- /dev/null
+++ b/include/mbgl/style/conversion/custom_geometry_source_options.hpp
@@ -0,0 +1,64 @@
+#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 {};
+ }
+ }
+
+ return { options };
+ }
+
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp
index 79b15dcfb0..8880d28fb1 100644
--- a/include/mbgl/style/conversion/data_driven_property_value.hpp
+++ b/include/mbgl/style/conversion/data_driven_property_value.hpp
@@ -4,6 +4,13 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+
+#include <unordered_set>
+
namespace mbgl {
namespace style {
@@ -11,10 +18,27 @@ namespace conversion {
template <class T>
struct Converter<DataDrivenPropertyValue<T>> {
- template <class V>
- optional<DataDrivenPropertyValue<T>> operator()(const V& value, Error& error) const {
+
+ optional<DataDrivenPropertyValue<T>> operator()(const Convertible& value, Error& error) const {
if (isUndefined(value)) {
return DataDrivenPropertyValue<T>();
+ } else if (expression::isExpression(value)) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(
+ value,
+ error,
+ valueTypeToExpressionType<T>());
+
+ if (!expression) {
+ return {};
+ }
+
+ if (isFeatureConstant(**expression)) {
+ return DataDrivenPropertyValue<T>(CameraFunction<T>(std::move(*expression)));
+ } else if (isZoomConstant(**expression)) {
+ return DataDrivenPropertyValue<T>(SourceFunction<T>(std::move(*expression)));
+ } else {
+ return DataDrivenPropertyValue<T>(CompositeFunction<T>(std::move(*expression)));
+ }
} else if (!isObject(value)) {
optional<T> constant = convert<T>(value, error);
if (!constant) {
diff --git a/include/mbgl/style/conversion/expression.hpp b/include/mbgl/style/conversion/expression.hpp
new file mode 100644
index 0000000000..c5fcf906a7
--- /dev/null
+++ b/include/mbgl/style/conversion/expression.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+using namespace mbgl::style::expression;
+
+template<> struct Converter<std::unique_ptr<Expression>> {
+ optional<std::unique_ptr<Expression>> operator()(const Convertible& value, Error& error, type::Type expected) const {
+ ParsingContext ctx(optional<type::Type> {expected});
+ ParseResult parsed = ctx.parse(value);
+ if (parsed) {
+ return std::move(*parsed);
+ }
+ std::string combinedError;
+ for (const ParsingError& parsingError : ctx.getErrors()) {
+ if (combinedError.size() > 0) {
+ combinedError += "\n";
+ }
+ if (parsingError.key.size() > 0) {
+ combinedError += parsingError.key + ": ";
+ }
+ combinedError += parsingError.message;
+ }
+ error = { combinedError };
+ return {};
+ };
+};
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp
index 986d1bf80d..9daf6ea7a4 100644
--- a/include/mbgl/style/conversion/filter.hpp
+++ b/include/mbgl/style/conversion/filter.hpp
@@ -2,7 +2,6 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/util/geometry.hpp>
namespace mbgl {
namespace style {
@@ -11,247 +10,7 @@ namespace conversion {
template <>
struct Converter<Filter> {
public:
- template <class V>
- optional<Filter> operator()(const V& value, Error& error) const {
- if (!isArray(value)) {
- error = { "filter expression must be an array" };
- return {};
- }
-
- if (arrayLength(value) < 1) {
- error = { "filter expression must have at least 1 element" };
- return {};
- }
-
- optional<std::string> op = toString(arrayMember(value, 0));
- if (!op) {
- error = { "filter operator must be a string" };
- return {};
- }
-
- if (*op == "==") {
- return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
- } else if (*op == "!=") {
- return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
- } else if (*op == ">") {
- return convertBinaryFilter<GreaterThanFilter>(value, error);
- } else if (*op == ">=") {
- return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
- } else if (*op == "<") {
- return convertBinaryFilter<LessThanFilter>(value, error);
- } else if (*op == "<=") {
- return convertBinaryFilter<LessThanEqualsFilter>(value, error);
- } else if (*op == "in") {
- return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
- } else if (*op == "!in") {
- return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
- } else if (*op == "all") {
- return convertCompoundFilter<AllFilter>(value, error);
- } else if (*op == "any") {
- return convertCompoundFilter<AnyFilter>(value, error);
- } else if (*op == "none") {
- return convertCompoundFilter<NoneFilter>(value, error);
- } else if (*op == "has") {
- return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
- } else if (*op == "!has") {
- return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
- }
-
- error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
- return {};
- }
-
-private:
- optional<Value> normalizeValue(const optional<Value>& value, Error& error) const {
- if (!value) {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- } else {
- return *value;
- }
- }
-
- template <class V>
- optional<FeatureType> toFeatureType(const V& value, Error& error) const {
- optional<std::string> type = toString(value);
- if (!type) {
- error = { "value for $type filter must be a string" };
- return {};
- } else if (*type == "Point") {
- return FeatureType::Point;
- } else if (*type == "LineString") {
- return FeatureType::LineString;
- } else if (*type == "Polygon") {
- return FeatureType::Polygon;
- } else {
- error = { "value for $type filter must be Point, LineString, or Polygon" };
- return {};
- }
- }
-
- template <class V>
- optional<FeatureIdentifier> toFeatureIdentifier(const V& value, Error& error) const {
- optional<Value> identifier = toValue(value);
- if (!identifier) {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- } else {
- return (*identifier).match(
- [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
- [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
- [] ( double t) -> optional<FeatureIdentifier> { return { t }; },
- [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
- [&] (const auto&) -> optional<FeatureIdentifier> {
- error = { "filter expression value must be a boolean, number, or string" };
- return {};
- });
- }
- }
-
- template <class FilterType, class IdentifierFilterType, class V>
- optional<Filter> convertUnaryFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 2) {
- error = { "filter expression must have 2 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$id") {
- return { IdentifierFilterType {} };
- } else {
- return { FilterType { *key } };
- }
- }
-
- template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
- optional<Filter> convertEqualityFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 3) {
- error = { "filter expression must have 3 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$type") {
- optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
- if (!filterValue) {
- return {};
- }
-
- return { TypeFilterType { *filterValue } };
-
- } else if (*key == "$id") {
- optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
- if (!filterValue) {
- return {};
- }
-
- return { IdentifierFilterType { *filterValue } };
-
- } else {
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
- if (!filterValue) {
- return {};
- }
-
- return { FilterType { *key, *filterValue } };
- }
- }
-
- template <class FilterType, class V>
- optional<Filter> convertBinaryFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 3) {
- error = { "filter expression must have 3 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
- if (!filterValue) {
- return {};
- }
-
- return { FilterType { *key, *filterValue } };
- }
-
- template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
- optional<Filter> convertSetFilter(const V& value, Error& error) const {
- if (arrayLength(value) < 2) {
- error = { "filter expression must at least 2 elements" };
- return {};
- }
-
- optional<std::string> key = toString(arrayMember(value, 1));
- if (!key) {
- error = { "filter expression key must be a string" };
- return {};
- }
-
- if (*key == "$type") {
- std::vector<FeatureType> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { TypeFilterType { std::move(values) } };
-
- } else if (*key == "$id") {
- std::vector<FeatureIdentifier> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { IdentifierFilterType { std::move(values) } };
-
- } else {
- std::vector<Value> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
- if (!filterValue) {
- return {};
- }
- values.push_back(*filterValue);
- }
-
- return { FilterType { *key, std::move(values) } };
- }
- }
-
- template <class FilterType, class V>
- optional<Filter> convertCompoundFilter(const V& value, Error& error) const {
- std::vector<Filter> filters;
- for (std::size_t i = 1; i < arrayLength(value); ++i) {
- optional<Filter> element = operator()(arrayMember(value, i), error);
- if (!element) {
- return {};
- }
- filters.push_back(*element);
- }
-
- return { FilterType { std::move(filters) } };
- }
+ optional<Filter> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp
index bf5b27a9a6..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,9 +149,8 @@ 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 {
- std::string type = util::Interpolatable<T> ? "exponential" : "interval";
+ optional<variant<Ts...>> operator()(const Convertible& value, Error& error) const {
+ std::string type = util::Interpolatable<T>::value ? "exponential" : "interval";
auto typeValue = objectMember(value, "type");
if (typeValue && toString(*typeValue)) {
@@ -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 19383d90ce..3f625babb6 100644
--- a/include/mbgl/style/conversion/geojson_options.hpp
+++ b/include/mbgl/style/conversion/geojson_options.hpp
@@ -9,74 +9,7 @@ namespace conversion {
template <>
struct Converter<GeoJSONOptions> {
-
- template <class V>
- optional<GeoJSONOptions> operator()(const V& value, Error& error) const {
- GeoJSONOptions options;
-
- 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/layer.hpp b/include/mbgl/style/conversion/layer.hpp
index 1fe467165d..1c0e2e2f07 100644
--- a/include/mbgl/style/conversion/layer.hpp
+++ b/include/mbgl/style/conversion/layer.hpp
@@ -1,220 +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 {};
- }
- 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 eaea2e89dc..0000000000
--- a/include/mbgl/style/conversion/make_property_setters.hpp
+++ /dev/null
@@ -1,207 +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, PropertyValue<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["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, PropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
- result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
- result["text-letter-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
- result["text-justify"] = &setProperty<V, SymbolLayer, PropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
- result["text-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TextAnchorType>, &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-stroke-width"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
- result["circle-stroke-width-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>;
- result["circle-stroke-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>;
- result["circle-stroke-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeColorTransition>;
- result["circle-stroke-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
- result["circle-stroke-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>;
-
- result["fill-extrusion-opacity"] = &setProperty<V, FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
- result["fill-extrusion-opacity-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>;
- result["fill-extrusion-color"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;
- result["fill-extrusion-color-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>;
- result["fill-extrusion-translate"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>;
- result["fill-extrusion-translate-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>;
- result["fill-extrusion-translate-anchor"] = &setProperty<V, FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>;
- result["fill-extrusion-translate-anchor-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>;
- result["fill-extrusion-pattern"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>;
- result["fill-extrusion-pattern-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>;
- result["fill-extrusion-height"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>;
- result["fill-extrusion-height-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>;
- result["fill-extrusion-base"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
- result["fill-extrusion-base-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>;
-
- result["raster-opacity"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
- result["raster-opacity-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterOpacityTransition>;
- result["raster-hue-rotate"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>;
- result["raster-hue-rotate-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterHueRotateTransition>;
- result["raster-brightness-min"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>;
- result["raster-brightness-min-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>;
- result["raster-brightness-max"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
- result["raster-brightness-max-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>;
- result["raster-saturation"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
- result["raster-saturation-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterSaturationTransition>;
- result["raster-contrast"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
- result["raster-contrast-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterContrastTransition>;
- result["raster-fade-duration"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
- result["raster-fade-duration-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterFadeDurationTransition>;
-
- result["background-color"] = &setProperty<V, BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
- result["background-color-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>;
- result["background-pattern"] = &setProperty<V, BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
- result["background-pattern-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>;
- result["background-opacity"] = &setProperty<V, BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
- result["background-opacity-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>;
-
- return result;
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp.ejs b/include/mbgl/style/conversion/make_property_setters.hpp.ejs
deleted file mode 100644
index 19c9f70538..0000000000
--- a/include/mbgl/style/conversion/make_property_setters.hpp.ejs
+++ /dev/null
@@ -1,48 +0,0 @@
-#pragma once
-
-// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
-
-#include <mbgl/style/conversion/property_setter.hpp>
-
-<% for (const layer of locals.layers) { -%>
-#include <mbgl/style/layers/<%- layer.type.replace('-', '_') %>_layer.hpp>
-<% } -%>
-
-#include <unordered_map>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <class V>
-auto makeLayoutPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
- result["visibility"] = &setVisibility<V>;
-
-<% for (const layer of locals.layers) { -%>
-<% for (const property of layer.layoutProperties) { -%>
- result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
-<% } -%>
-
-<% } -%>
- return result;
-}
-
-template <class V>
-auto makePaintPropertySetters() {
- std::unordered_map<std::string, PropertySetter<V>> result;
-
-<% for (const layer of locals.layers) { -%>
-<% for (const property of layer.paintProperties) { -%>
- result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
- result["<%- property.name %>-transition"] = &setTransition<V, <%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>;
-<% } -%>
-
-<% } -%>
- return result;
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/include/mbgl/style/conversion/position.hpp b/include/mbgl/style/conversion/position.hpp
index 7036b03822..044c45862d 100644
--- a/include/mbgl/style/conversion/position.hpp
+++ b/include/mbgl/style/conversion/position.hpp
@@ -2,9 +2,6 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/position.hpp>
-#include <mbgl/util/optional.hpp>
-
-#include <array>
namespace mbgl {
namespace style {
@@ -12,16 +9,7 @@ namespace conversion {
template <>
struct Converter<Position> {
- template <class V>
- optional<Position> operator()(const V& value, Error& error) const {
- optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error);
-
- if (!spherical) {
- return {};
- }
-
- return Position(*spherical);
- }
+ optional<Position> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp
index f8937da07d..97117de2ec 100644
--- a/include/mbgl/style/conversion/property_value.hpp
+++ b/include/mbgl/style/conversion/property_value.hpp
@@ -4,6 +4,11 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/conversion/expression.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
namespace mbgl {
namespace style {
@@ -11,10 +16,20 @@ namespace conversion {
template <class T>
struct Converter<PropertyValue<T>> {
- template <class V>
- optional<PropertyValue<T>> operator()(const V& value, Error& error) const {
+ optional<PropertyValue<T>> operator()(const Convertible& value, Error& error) const {
if (isUndefined(value)) {
return PropertyValue<T>();
+ } else if (isExpression(value)) {
+ optional<std::unique_ptr<Expression>> expression = convert<std::unique_ptr<Expression>>(value, error, valueTypeToExpressionType<T>());
+ if (!expression) {
+ return {};
+ }
+ if (isFeatureConstant(**expression)) {
+ return { CameraFunction<T>(std::move(*expression)) };
+ } else {
+ error = { "property expressions not supported" };
+ return {};
+ }
} else if (isObject(value)) {
optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error);
if (!function) {
diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp
index e0563ac10b..2cf2e36da4 100644
--- a/include/mbgl/style/conversion/source.hpp
+++ b/include/mbgl/style/conversion/source.hpp
@@ -1,16 +1,9 @@
#pragma once
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/coordinate.hpp>
-#include <mbgl/style/conversion/geojson.hpp>
-#include <mbgl/style/conversion/geojson_options.hpp>
-#include <mbgl/style/conversion/tileset.hpp>
#include <mbgl/style/source.hpp>
-#include <mbgl/style/sources/geojson_source.hpp>
-#include <mbgl/style/sources/raster_source.hpp>
-#include <mbgl/style/sources/vector_source.hpp>
-#include <mbgl/style/sources/image_source.hpp>
-#include <mbgl/util/geo.hpp>
+
+#include <memory>
namespace mbgl {
namespace style {
@@ -19,170 +12,7 @@ namespace conversion {
template <>
struct Converter<std::unique_ptr<Source>> {
public:
-
- template <class V>
- optional<std::unique_ptr<Source>> operator()(const V& value, Error& error, const std::string& id) const {
- if (!isObject(value)) {
- error = { "source must be an object" };
- return {};
- }
-
- auto typeValue = objectMember(value, "type");
- if (!typeValue) {
- error = { "source must have a type" };
- return {};
- }
-
- optional<std::string> type = toString(*typeValue);
- if (!type) {
- error = { "source type must be a string" };
- return {};
- }
-
- if (*type == "raster") {
- return convertRasterSource(id, value, error);
- } else if (*type == "vector") {
- return convertVectorSource(id, value, error);
- } else if (*type == "geojson") {
- return convertGeoJSONSource(id, value, error);
- } else if (*type == "image") {
- return convertImageSource(id, value, error);
- } else {
- error = { "invalid source type" };
- return {};
- }
- }
-
-private:
- // A tile source can either specify a URL to TileJSON, or inline TileJSON.
- template <class V>
- optional<variant<std::string, Tileset>> convertURLOrTileset(const V& value, Error& error) const {
- auto urlVal = objectMember(value, "url");
- if (!urlVal) {
- optional<Tileset> tileset = convert<Tileset>(value, error);
- if (!tileset) {
- return {};
- }
- return { *tileset };
- }
-
- optional<std::string> url = toString(*urlVal);
- if (!url) {
- error = { "source url must be a string" };
- return {};
- }
-
- return { *url };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id,
- const V& value,
- Error& error) const {
- optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
- if (!urlOrTileset) {
- return {};
- }
-
- uint16_t tileSize = util::tileSize;
- auto tileSizeValue = objectMember(value, "tileSize");
- if (tileSizeValue) {
- optional<float> size = toNumber(*tileSizeValue);
- if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
- error = { "invalid tileSize" };
- return {};
- }
- tileSize = *size;
- }
-
- return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id,
- const V& value,
- Error& error) const {
- optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
- if (!urlOrTileset) {
- return {};
- }
-
- return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
- const V& value,
- Error& error) const {
- auto dataValue = objectMember(value, "data");
- if (!dataValue) {
- error = { "GeoJSON source must have a data value" };
- return {};
- }
-
- optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error);
- if (!options) {
- return {};
- }
-
- auto result = std::make_unique<GeoJSONSource>(id, *options);
-
- if (isObject(*dataValue)) {
- optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error);
- if (!geoJSON) {
- return {};
- }
- result->setGeoJSON(std::move(*geoJSON));
- } else if (toString(*dataValue)) {
- result->setURL(*toString(*dataValue));
- } else {
- error = { "GeoJSON data must be a URL or an object" };
- return {};
- }
-
- return { std::move(result) };
- }
-
- template <class V>
- optional<std::unique_ptr<Source>> convertImageSource(const std::string& id,
- const V& value,
- Error& error) const {
- auto urlValue = objectMember(value, "url");
- if (!urlValue) {
- error = { "Image source must have a url value" };
- return {};
- }
-
- auto urlString = toString(*urlValue);
- if (!urlString) {
- error = { "Image url must be a URL string" };
- return {};
- }
-
- auto coordinatesValue = objectMember(value, "coordinates");
- if (!coordinatesValue) {
- error = { "Image source must have a coordinates values" };
- return {};
- }
-
- if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) {
- error = { "Image coordinates must be an array of four longitude latitude pairs" };
- return {};
- }
-
- std::array<LatLng, 4> coordinates;
- for (std::size_t i=0; i < 4; i++) {
- auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error);
- if (!latLng) {
- return {};
- }
- coordinates[i] = *latLng;
- }
- auto result = std::make_unique<ImageSource>(id, coordinates);
- result->setURL(*urlString);
-
- return { std::move(result) };
- }
+ optional<std::unique_ptr<Source>> operator()(const Convertible& value, Error& error, const std::string& id) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp
index 377170aa6a..1fb4acf70d 100644
--- a/include/mbgl/style/conversion/tileset.hpp
+++ b/include/mbgl/style/conversion/tileset.hpp
@@ -10,70 +10,7 @@ namespace conversion {
template <>
struct Converter<Tileset> {
public:
- 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);
- }
-
- return result;
- }
+ optional<Tileset> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp
index de8834d578..0563f39ac3 100644
--- a/include/mbgl/style/conversion/transition_options.hpp
+++ b/include/mbgl/style/conversion/transition_options.hpp
@@ -10,37 +10,7 @@ namespace conversion {
template <>
struct Converter<TransitionOptions> {
public:
- template <class V>
- optional<TransitionOptions> operator()(const V& value, Error& error) const {
- if (!isObject(value)) {
- error = { "transition must be an object" };
- return {};
- }
-
- TransitionOptions result;
-
- auto duration = objectMember(value, "duration");
- if (duration) {
- auto number = toNumber(*duration);
- if (!number) {
- error = { "duration must be a number" };
- return {};
- }
- result.duration = { std::chrono::milliseconds(int64_t(*number)) };
- }
-
- auto delay = objectMember(value, "delay");
- if (delay) {
- auto number = toNumber(*delay);
- if (!number) {
- error = { "delay must be a number" };
- return {};
- }
- result.delay = { std::chrono::milliseconds(int64_t(*number)) };
- }
-
- return result;
- }
+ optional<TransitionOptions> operator()(const Convertible& value, Error& error) const;
};
} // namespace conversion
diff --git a/include/mbgl/style/expression/array_assertion.hpp b/include/mbgl/style/expression/array_assertion.hpp
new file mode 100644
index 0000000000..2516eea024
--- /dev/null
+++ b/include/mbgl/style/expression/array_assertion.hpp
@@ -0,0 +1,39 @@
+#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;
+ }
+
+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..504d49f4e5
--- /dev/null
+++ b/include/mbgl/style/expression/assertion.hpp
@@ -0,0 +1,33 @@
+#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;
+
+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..e3eefa4fe8
--- /dev/null
+++ b/include/mbgl/style/expression/at.hpp
@@ -0,0 +1,38 @@
+#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;
+ }
+
+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..01231d706b
--- /dev/null
+++ b/include/mbgl/style/expression/boolean_operator.hpp
@@ -0,0 +1,49 @@
+#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;
+
+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;
+
+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..ece2fe0329
--- /dev/null
+++ b/include/mbgl/style/expression/case.hpp
@@ -0,0 +1,36 @@
+#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;
+
+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..4e6a9b3793
--- /dev/null
+++ b/include/mbgl/style/expression/coalesce.hpp
@@ -0,0 +1,45 @@
+#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::size_t getLength() const {
+ return args.size();
+ }
+
+ Expression* getChild(std::size_t i) const {
+ return args.at(i).get();
+ }
+
+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..665bb7ce7c
--- /dev/null
+++ b/include/mbgl/style/expression/coercion.hpp
@@ -0,0 +1,34 @@
+#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;
+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..fc3edbfd4a
--- /dev/null
+++ b/include/mbgl/style/expression/compound_expression.hpp
@@ -0,0 +1,138 @@
+#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_) :
+ result(std::move(result_)),
+ params(std::move(params_))
+ {}
+ virtual ~SignatureBase() = default;
+ virtual std::unique_ptr<Expression> makeExpression(const std::string& name, std::vector<std::unique_ptr<Expression>>) const = 0;
+ type::Type result;
+ variant<std::vector<type::Type>, VarargsType> params;
+};
+} // 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(); }
+ );
+ }
+
+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;
+ }
+
+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 std::string& name,
+ 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/expression.hpp b/include/mbgl/style/expression/expression.hpp
new file mode 100644
index 0000000000..a22fc28724
--- /dev/null
+++ b/include/mbgl/style/expression/expression.hpp
@@ -0,0 +1,173 @@
+#pragma once
+
+#include <array>
+#include <vector>
+#include <memory>
+#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>
+
+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;
+
+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..439122f91c
--- /dev/null
+++ b/include/mbgl/style/expression/interpolate.hpp
@@ -0,0 +1,177 @@
+#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>
+
+
+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; }
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override {
+ visit(*input);
+ for (const auto& stop : stops) {
+ visit(*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); }
+ );
+ }
+
+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 (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;
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/is_constant.hpp b/include/mbgl/style/expression/is_constant.hpp
new file mode 100644
index 0000000000..29e03ccbc0
--- /dev/null
+++ b/include/mbgl/style/expression/is_constant.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template <typename T>
+bool isGlobalPropertyConstant(const Expression& expression, const T& properties) {
+ if (auto e = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ for (const std::string& property : properties) {
+ if (e->getName() == property) {
+ return false;
+ }
+ }
+ }
+
+ bool isConstant = true;
+ expression.eachChild([&](const Expression& e) {
+ if (isConstant && !isGlobalPropertyConstant(e, properties)) {
+ isConstant = false;
+ }
+ });
+ return isConstant;
+};
+
+bool isFeatureConstant(const Expression& expression);
+bool isZoomConstant(const Expression& e);
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/is_expression.hpp b/include/mbgl/style/expression/is_expression.hpp
new file mode 100644
index 0000000000..77c489619c
--- /dev/null
+++ b/include/mbgl/style/expression/is_expression.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isExpression(const conversion::Convertible& value);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/let.hpp b/include/mbgl/style/expression/let.hpp
new file mode 100644
index 0000000000..aaa16ca0c2
--- /dev/null
+++ b/include/mbgl/style/expression/let.hpp
@@ -0,0 +1,72 @@
+#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;
+ }
+
+ Expression* getResult() const {
+ return result.get();
+ }
+
+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;
+ }
+
+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..a0819c7e73
--- /dev/null
+++ b/include/mbgl/style/expression/literal.hpp
@@ -0,0 +1,38 @@
+#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;
+ }
+
+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..e17fe96bfe
--- /dev/null
+++ b/include/mbgl/style/expression/match.hpp
@@ -0,0 +1,45 @@
+#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_))
+ {}
+
+ void eachChild(const std::function<void(const Expression&)>& visit) const override;
+
+ bool operator==(const Expression& e) const override;
+
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+
+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..a983789cbd
--- /dev/null
+++ b/include/mbgl/style/expression/parsing_context.hpp
@@ -0,0 +1,148 @@
+#pragma once
+
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/conversion.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+#include <memory>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class Expression;
+
+struct ParsingError {
+ std::string message;
+ std::string key;
+ bool operator==(const ParsingError& rhs) const { return message == rhs.message && key == rhs.key; }
+};
+
+using ParseResult = optional<std::unique_ptr<Expression>>;
+
+namespace detail {
+
+class Scope {
+public:
+ Scope(const std::map<std::string, std::shared_ptr<Expression>>& bindings_, std::shared_ptr<Scope> parent_ = nullptr) :
+ bindings(bindings_),
+ parent(std::move(parent_))
+ {}
+
+ const std::map<std::string, std::shared_ptr<Expression>>& bindings;
+ std::shared_ptr<Scope> parent;
+
+ optional<std::shared_ptr<Expression>> get(const std::string& name) {
+ auto it = bindings.find(name);
+ if (it != bindings.end()) {
+ return {it->second};
+ } else if (parent) {
+ return parent->get(name);
+ } else {
+ return optional<std::shared_ptr<Expression>>();
+ }
+ }
+};
+
+} // namespace detail
+
+class ParsingContext {
+public:
+ ParsingContext() : errors(std::make_shared<std::vector<ParsingError>>()) {}
+ ParsingContext(std::string key_) : key(std::move(key_)), errors(std::make_shared<std::vector<ParsingError>>()) {}
+ explicit ParsingContext(optional<type::Type> expected_)
+ : expected(std::move(expected_)),
+ errors(std::make_shared<std::vector<ParsingError>>())
+ {}
+ ParsingContext(ParsingContext&&) = default;
+
+ ParsingContext(const ParsingContext&) = delete;
+ ParsingContext& operator=(const ParsingContext&) = delete;
+
+ std::string getKey() const { return key; }
+ optional<type::Type> getExpected() const { return expected; }
+ const std::vector<ParsingError>& getErrors() const { return *errors; }
+
+ /*
+ 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);
+
+ /*
+ Parse a child expression.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible&,
+ std::size_t,
+ optional<type::Type> = {});
+
+ /*
+ Parse a child expression.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible&,
+ std::size_t index,
+ optional<type::Type>,
+ const std::map<std::string, std::shared_ptr<Expression>>&);
+
+ /*
+ Check whether `t` is a subtype of `expected`, collecting an error if not.
+ */
+ optional<std::string> checkType(const type::Type& t);
+
+ optional<std::shared_ptr<Expression>> getBinding(const std::string name) {
+ if (!scope) return optional<std::shared_ptr<Expression>>();
+ return scope->get(name);
+ }
+
+ void error(std::string message) {
+ errors->push_back({message, key});
+ }
+
+ void error(std::string message, std::size_t child) {
+ errors->push_back({message, key + "[" + util::toString(child) + "]"});
+ }
+
+ void error(std::string message, std::size_t child, std::size_t grandchild) {
+ errors->push_back({message, key + "[" + util::toString(child) + "][" + util::toString(grandchild) + "]"});
+ }
+
+ void appendErrors(ParsingContext&& ctx) {
+ errors->reserve(errors->size() + ctx.errors->size());
+ std::move(ctx.errors->begin(), ctx.errors->end(), std::inserter(*errors, errors->end()));
+ ctx.errors->clear();
+ }
+
+ void clearErrors() {
+ errors->clear();
+ }
+
+private:
+ ParsingContext(std::string key_,
+ std::shared_ptr<std::vector<ParsingError>> errors_,
+ optional<type::Type> expected_,
+ std::shared_ptr<detail::Scope> scope_)
+ : key(std::move(key_)),
+ expected(std::move(expected_)),
+ scope(std::move(scope_)),
+ errors(std::move(errors_))
+ {}
+
+ std::string key;
+ optional<type::Type> expected;
+ std::shared_ptr<detail::Scope> scope;
+ std::shared_ptr<std::vector<ParsingError>> errors;
+};
+
+using ParseFunction = ParseResult (*)(const conversion::Convertible&, ParsingContext&);
+using ExpressionRegistry = std::unordered_map<std::string, ParseFunction>;
+const ExpressionRegistry& getExpressionRegistry();
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/step.hpp b/include/mbgl/style/expression/step.hpp
new file mode 100644
index 0000000000..e3c49bc609
--- /dev/null
+++ b/include/mbgl/style/expression/step.hpp
@@ -0,0 +1,45 @@
+
+#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;
+
+ 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;
+
+ static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
+
+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..8baa9d2dba
--- /dev/null
+++ b/include/mbgl/style/expression/value.hpp
@@ -0,0 +1,153 @@
+#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);
+};
+
+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);
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp
index 0fd5bcb078..25b38e3616 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -1,10 +1,18 @@
#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>
#include <mbgl/util/variant.hpp>
+
namespace mbgl {
namespace style {
@@ -12,30 +20,66 @@ template <class T>
class CameraFunction {
public:
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
ExponentialStops<T>,
IntervalStops<T>>,
variant<
IntervalStops<T>>>;
+
+ CameraFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_)),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {
+ assert(!expression::isZoomConstant(*expression));
+ assert(expression::isFeatureConstant(*expression));
+ }
CameraFunction(Stops stops_)
- : stops(std::move(stops_)) {
- }
+ : stops(std::move(stops_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(s);
+ })),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {}
T evaluate(float zoom) const {
- return stops.match([&] (const auto& s) {
- return s.evaluate(zoom).value_or(T());
- });
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext(zoom, nullptr));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : T();
+ }
+ return T();
+ }
+
+ float interpolationFactor(const Range<float>& inputLevels, const float inputValue) const {
+ return zoomCurve.match(
+ [&](const expression::InterpolateBase* z) {
+ return z->interpolationFactor(Range<double> { inputLevels.min, inputLevels.max }, inputValue);
+ },
+ [&](const expression::Step*) { return 0.0f; }
+ );
+ }
+
+ Range<float> getCoveringStops(const float lower, const float upper) const {
+ return zoomCurve.match(
+ [&](auto z) { return z->getCoveringStops(lower, upper); }
+ );
}
friend bool operator==(const CameraFunction& lhs,
const CameraFunction& rhs) {
- return lhs.stops == rhs.stops;
+ return *lhs.expression == *rhs.expression;
}
- Stops stops;
bool useIntegerZoom = false;
+
+ // retained for compatibility with pre-expression function API
+ Stops stops;
+
+private:
+ std::shared_ptr<expression::Expression> expression;
+ const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve;
};
} // namespace style
diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp
index 43599cd333..b44bf8e6fe 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>
@@ -24,7 +31,7 @@ template <class T>
class CompositeFunction {
public:
using InnerStops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
ExponentialStops<T>,
IntervalStops<T>,
@@ -34,7 +41,7 @@ public:
CategoricalStops<T>>>;
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
CompositeExponentialStops<T>,
CompositeIntervalStops<T>,
@@ -43,110 +50,71 @@ public:
CompositeIntervalStops<T>,
CompositeCategoricalStops<T>>>;
- CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
- : property(std::move(property_)),
- stops(std::move(stops_)),
- defaultValue(std::move(defaultValue_)) {
- }
-
- struct CoveringRanges {
- float zoom;
- Range<float> coveringZoomRange;
- Range<InnerStops> coveringStopsRange;
- };
-
- // Return the relevant stop zoom values and inner stops that bracket a given zoom level. This
- // is the first step toward evaluating the function, and is used for in the course of both partial
- // evaluation of data-driven paint properties, and full evaluation of data-driven layout properties.
- CoveringRanges coveringRanges(float zoom) const {
- return stops.match(
- [&] (const auto& s) {
- assert(!s.stops.empty());
- auto minIt = s.stops.lower_bound(zoom);
- auto maxIt = s.stops.upper_bound(zoom);
-
- // lower_bound yields first element >= zoom, but we want the *last*
- // element <= zoom, so if we found a stop > zoom, back up by one.
- if (minIt != s.stops.begin() && minIt != s.stops.end() && minIt->first > zoom) {
- minIt--;
- }
-
- return CoveringRanges {
- zoom,
- Range<float> {
- minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first,
- maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first
- },
- Range<InnerStops> {
- s.innerStops(minIt == s.stops.end() ? s.stops.rbegin()->second : minIt->second),
- s.innerStops(maxIt == s.stops.end() ? s.stops.rbegin()->second : maxIt->second)
- }
- };
- }
- );
+ CompositeFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_)),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {
+ assert(!expression::isZoomConstant(*expression));
+ assert(!expression::isFeatureConstant(*expression));
}
- // Given 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)
- };
- }
-
- // Given the covering ranges for range of zoom values (typically two adjacent integer zoom levels,
- // e.g. 5.0 and 6.0), and a feature, return the results of fully evaluating the function for that
- // feature at each of the two zoom levels. These two results are what go into the paint vertex buffers
- // for vertices associated with this feature. The shader will interpolate between them at render time.
+ CompositeFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
+ : property(std::move(property_)),
+ stops(std::move(stops_)),
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(property, s);
+ })),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {}
+
+ // Return the range obtained by evaluating the function at each of the zoom levels in zoomRange
template <class Feature>
- Range<T> evaluate(const Range<CoveringRanges>& ranges, const Feature& feature, T finalDefaultValue) {
- optional<Value> value = feature.getValue(property);
- if (!value) {
- return Range<T> {
- defaultValue.value_or(finalDefaultValue),
- defaultValue.value_or(finalDefaultValue)
- };
- }
+ Range<T> evaluate(const Range<float>& zoomRange, const Feature& feature, T finalDefaultValue) {
return Range<T> {
- evaluateFinal(ranges.min, *value, finalDefaultValue),
- evaluateFinal(ranges.max, *value, finalDefaultValue)
+ evaluate(zoomRange.min, feature, finalDefaultValue),
+ evaluate(zoomRange.max, feature, finalDefaultValue)
};
}
- // Fully evaluate the function for a zoom value and feature. This is used when evaluating data-driven
- // layout properties.
template <class Feature>
T evaluate(float zoom, const Feature& feature, T finalDefaultValue) const {
- optional<Value> value = feature.getValue(property);
- if (!value) {
- return defaultValue.value_or(finalDefaultValue);
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext({zoom}, &feature));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue;
}
- return evaluateFinal(coveringRanges(zoom), *value, finalDefaultValue);
+ return defaultValue ? *defaultValue : finalDefaultValue;
+ }
+
+ float interpolationFactor(const Range<float>& inputLevels, const float inputValue) const {
+ return zoomCurve.match(
+ [&](const expression::InterpolateBase* z) {
+ return z->interpolationFactor(Range<double> { inputLevels.min, inputLevels.max }, inputValue);
+ },
+ [&](const expression::Step*) { return 0.0f; }
+ );
+ }
+
+ Range<float> getCoveringStops(const float lower, const float upper) const {
+ return zoomCurve.match(
+ [&](auto z) { return z->getCoveringStops(lower, upper); }
+ );
}
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;
-
+
private:
- T evaluateFinal(const CoveringRanges& ranges, const Value& value, T finalDefaultValue) const {
- auto eval = [&] (const auto& s) {
- return s.evaluate(value).value_or(defaultValue.value_or(finalDefaultValue));
- };
- return util::interpolate(
- ranges.coveringStopsRange.min.match(eval),
- ranges.coveringStopsRange.max.match(eval),
- util::interpolationFactor(1.0f, ranges.coveringZoomRange, ranges.zoom));
- }
+ std::shared_ptr<expression::Expression> expression;
+ const variant<const expression::InterpolateBase*, const expression::Step*> zoomCurve;
};
} // namespace style
diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp
new file mode 100644
index 0000000000..9f7b7ed1f8
--- /dev/null
+++ b/include/mbgl/style/function/convert.hpp
@@ -0,0 +1,351 @@
+#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};
+ }
+
+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 2872c63a64..02e4b604e2 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>
@@ -16,7 +18,7 @@ template <class T>
class SourceFunction {
public:
using Stops = std::conditional_t<
- util::Interpolatable<T>,
+ util::Interpolatable<T>::value,
variant<
ExponentialStops<T>,
IntervalStops<T>,
@@ -27,33 +29,48 @@ public:
CategoricalStops<T>,
IdentityStops<T>>>;
+ SourceFunction(std::unique_ptr<expression::Expression> expression_)
+ : expression(std::move(expression_))
+ {
+ assert(expression::isZoomConstant(*expression));
+ assert(!expression::isFeatureConstant(*expression));
+ }
+
SourceFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
: property(std::move(property_)),
stops(std::move(stops_)),
- defaultValue(std::move(defaultValue_)) {
- }
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const IdentityStops<T>&) {
+ return expression::Convert::fromIdentityFunction(expression::valueTypeToExpressionType<T>(), property);
+ }, [&] (const auto& s) {
+ return expression::Convert::toExpression(property, s);
+ }))
+ {}
template <class Feature>
T evaluate(const Feature& feature, T finalDefaultValue) const {
- optional<Value> v = feature.getValue(property);
- if (!v) {
- return defaultValue.value_or(finalDefaultValue);
+ const expression::EvaluationResult result = expression->evaluate(expression::EvaluationContext(&feature));
+ if (result) {
+ const optional<T> typed = expression::fromExpressionValue<T>(*result);
+ return typed ? *typed : defaultValue ? *defaultValue : finalDefaultValue;
}
- return stops.match([&] (const auto& s) -> T {
- return s.evaluate(*v).value_or(defaultValue.value_or(finalDefaultValue));
- });
+ return defaultValue ? *defaultValue : finalDefaultValue;
}
friend bool operator==(const SourceFunction& lhs,
const SourceFunction& rhs) {
- return std::tie(lhs.property, lhs.stops, lhs.defaultValue)
- == std::tie(rhs.property, rhs.stops, rhs.defaultValue);
+ return *lhs.expression == *rhs.expression;
}
+ bool useIntegerZoom = false;
+
+ // retained for compatibility with pre-expression function API
std::string property;
Stops stops;
optional<T> defaultValue;
- bool useIntegerZoom = false;
+
+private:
+ std::shared_ptr<expression::Expression> expression;
};
} // namespace style
diff --git a/include/mbgl/style/layer.hpp b/include/mbgl/style/layer.hpp
index c6a3c0e735..eb2dbf830b 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>
@@ -126,7 +126,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/layers/circle_layer.hpp b/include/mbgl/style/layers/circle_layer.hpp
index 275a2e67a5..942dd67503 100644
--- a/include/mbgl/style/layers/circle_layer.hpp
+++ b/include/mbgl/style/layers/circle_layer.hpp
@@ -78,6 +78,12 @@ public:
void setCirclePitchScaleTransition(const TransitionOptions&);
TransitionOptions getCirclePitchScaleTransition() const;
+ static PropertyValue<AlignmentType> getDefaultCirclePitchAlignment();
+ PropertyValue<AlignmentType> getCirclePitchAlignment() const;
+ void setCirclePitchAlignment(PropertyValue<AlignmentType>);
+ void setCirclePitchAlignmentTransition(const TransitionOptions&);
+ TransitionOptions getCirclePitchAlignmentTransition() const;
+
static DataDrivenPropertyValue<float> getDefaultCircleStrokeWidth();
DataDrivenPropertyValue<float> getCircleStrokeWidth() const;
void setCircleStrokeWidth(DataDrivenPropertyValue<float>);
diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp
index 79a353b047..bf3387f95b 100644
--- a/include/mbgl/style/layers/custom_layer.hpp
+++ b/include/mbgl/style/layers/custom_layer.hpp
@@ -39,6 +39,14 @@ struct CustomLayerRenderParameters {
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.
*
@@ -51,8 +59,16 @@ public:
CustomLayer(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
+ CustomLayerContextLostFunction,
CustomLayerDeinitializeFunction,
void* context);
+
+ CustomLayer(const std::string& id,
+ CustomLayerInitializeFunction,
+ CustomLayerRenderFunction,
+ CustomLayerDeinitializeFunction,
+ void* context);
+
~CustomLayer() final;
// Visibility
diff --git a/include/mbgl/style/layers/line_layer.hpp b/include/mbgl/style/layers/line_layer.hpp
index 0b49690fd8..4519296323 100644
--- a/include/mbgl/style/layers/line_layer.hpp
+++ b/include/mbgl/style/layers/line_layer.hpp
@@ -42,9 +42,9 @@ public:
PropertyValue<LineCapType> getLineCap() const;
void setLineCap(PropertyValue<LineCapType>);
- static PropertyValue<LineJoinType> getDefaultLineJoin();
- PropertyValue<LineJoinType> getLineJoin() const;
- void setLineJoin(PropertyValue<LineJoinType>);
+ static DataDrivenPropertyValue<LineJoinType> getDefaultLineJoin();
+ DataDrivenPropertyValue<LineJoinType> getLineJoin() const;
+ void setLineJoin(DataDrivenPropertyValue<LineJoinType>);
static PropertyValue<float> getDefaultLineMiterLimit();
PropertyValue<float> getLineMiterLimit() const;
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index 8158f267c9..a72baa0b4e 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -98,6 +98,14 @@ public:
DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const;
void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>);
+ static DataDrivenPropertyValue<SymbolAnchorType> getDefaultIconAnchor();
+ DataDrivenPropertyValue<SymbolAnchorType> getIconAnchor() const;
+ void setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType>);
+
+ static PropertyValue<AlignmentType> getDefaultIconPitchAlignment();
+ PropertyValue<AlignmentType> getIconPitchAlignment() const;
+ void setIconPitchAlignment(PropertyValue<AlignmentType>);
+
static PropertyValue<AlignmentType> getDefaultTextPitchAlignment();
PropertyValue<AlignmentType> getTextPitchAlignment() const;
void setTextPitchAlignment(PropertyValue<AlignmentType>);
@@ -118,25 +126,25 @@ public:
DataDrivenPropertyValue<float> getTextSize() const;
void setTextSize(DataDrivenPropertyValue<float>);
- static PropertyValue<float> getDefaultTextMaxWidth();
- PropertyValue<float> getTextMaxWidth() const;
- void setTextMaxWidth(PropertyValue<float>);
+ static DataDrivenPropertyValue<float> getDefaultTextMaxWidth();
+ DataDrivenPropertyValue<float> getTextMaxWidth() const;
+ void setTextMaxWidth(DataDrivenPropertyValue<float>);
static PropertyValue<float> getDefaultTextLineHeight();
PropertyValue<float> getTextLineHeight() const;
void setTextLineHeight(PropertyValue<float>);
- static PropertyValue<float> getDefaultTextLetterSpacing();
- PropertyValue<float> getTextLetterSpacing() const;
- void setTextLetterSpacing(PropertyValue<float>);
+ static DataDrivenPropertyValue<float> getDefaultTextLetterSpacing();
+ DataDrivenPropertyValue<float> getTextLetterSpacing() const;
+ void setTextLetterSpacing(DataDrivenPropertyValue<float>);
- static PropertyValue<TextJustifyType> getDefaultTextJustify();
- PropertyValue<TextJustifyType> getTextJustify() const;
- void setTextJustify(PropertyValue<TextJustifyType>);
+ static DataDrivenPropertyValue<TextJustifyType> getDefaultTextJustify();
+ DataDrivenPropertyValue<TextJustifyType> getTextJustify() const;
+ void setTextJustify(DataDrivenPropertyValue<TextJustifyType>);
- static PropertyValue<TextAnchorType> getDefaultTextAnchor();
- PropertyValue<TextAnchorType> getTextAnchor() const;
- void setTextAnchor(PropertyValue<TextAnchorType>);
+ static DataDrivenPropertyValue<SymbolAnchorType> getDefaultTextAnchor();
+ DataDrivenPropertyValue<SymbolAnchorType> getTextAnchor() const;
+ void setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType>);
static PropertyValue<float> getDefaultTextMaxAngle();
PropertyValue<float> getTextMaxAngle() const;
diff --git a/include/mbgl/style/source.hpp b/include/mbgl/style/source.hpp
index cec9619451..0b6a6c72d9 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>
@@ -76,7 +76,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..a0b990b44b
--- /dev/null
+++ b/include/mbgl/style/sources/custom_geometry_source.hpp
@@ -0,0 +1,56 @@
+#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;
+ };
+
+ 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 2dcfec51aa..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 {
@@ -12,7 +13,9 @@ namespace style {
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/image_source.hpp b/include/mbgl/style/sources/image_source.hpp
index d8a2c45bd8..009764291f 100644
--- a/include/mbgl/style/sources/image_source.hpp
+++ b/include/mbgl/style/sources/image_source.hpp
@@ -18,7 +18,7 @@ public:
optional<std::string> getURL() const;
void setURL(const std::string& url);
- void setImage(UnassociatedImage&&);
+ void setImage(PremultipliedImage&&);
void setCoordinates(const std::array<LatLng, 4>&);
std::array<LatLng, 4> getCoordinates() const;
diff --git a/include/mbgl/style/style.hpp b/include/mbgl/style/style.hpp
index cb84922b4d..d6fdbd8f2c 100644
--- a/include/mbgl/style/style.hpp
+++ b/include/mbgl/style/style.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/style/transition_options.hpp>
+#include <mbgl/map/camera.hpp>
#include <mbgl/util/geo.hpp>
#include <string>
@@ -32,10 +33,7 @@ public:
// Defaults
std::string getName() const;
- LatLng getDefaultLatLng() const;
- double getDefaultZoom() const;
- double getDefaultBearing() const;
- double getDefaultPitch() const;
+ CameraOptions getDefaultCamera() const;
// TransitionOptions
TransitionOptions getTransitionOptions() const;
diff --git a/include/mbgl/style/transition_options.hpp b/include/mbgl/style/transition_options.hpp
index 1583667025..87a81717a0 100644
--- a/include/mbgl/style/transition_options.hpp
+++ b/include/mbgl/style/transition_options.hpp
@@ -8,8 +8,13 @@ namespace style {
class TransitionOptions {
public:
- optional<Duration> duration = {};
- optional<Duration> delay = {};
+ optional<Duration> duration;
+ optional<Duration> delay;
+
+ TransitionOptions(optional<Duration> duration_ = {},
+ optional<Duration> delay_ = {})
+ : duration(std::move(duration_)),
+ delay(std::move(delay_)) {}
TransitionOptions reverseMerge(const TransitionOptions& defaults) const {
return {
diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp
index 44b16f16e7..6fe457e181 100644
--- a/include/mbgl/style/types.hpp
+++ b/include/mbgl/style/types.hpp
@@ -4,18 +4,19 @@
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,
GeoJSON,
Video,
Annotations,
- Image
+ Image,
+ CustomVector
};
-namespace style {
-
enum class VisibilityType : bool {
Visible,
None,
@@ -68,7 +69,7 @@ enum class TextJustifyType : uint8_t {
Right
};
-enum class TextAnchorType : uint8_t {
+enum class SymbolAnchorType : uint8_t {
Center,
Left,
Right,
diff --git a/src/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp
index 1ce3eea98e..11fb5ce537 100644
--- a/src/mbgl/tile/tile_id.hpp
+++ b/include/mbgl/tile/tile_id.hpp
@@ -4,11 +4,11 @@
#include <cstdint>
#include <array>
+#include <tuple>
#include <forward_list>
#include <algorithm>
#include <iosfwd>
#include <cassert>
-#include <boost/functional/hash.hpp>
namespace mbgl {
@@ -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);
@@ -46,8 +46,8 @@ std::string toString(const CanonicalTileID&);
// z/x/y describe the
class OverscaledTileID {
public:
- OverscaledTileID(uint8_t overscaledZ, CanonicalTileID);
- OverscaledTileID(uint8_t overscaledZ, uint8_t z, uint32_t x, uint32_t y);
+ OverscaledTileID(uint8_t overscaledZ, int16_t wrap, CanonicalTileID);
+ OverscaledTileID(uint8_t overscaledZ, int16_t wrap, uint8_t z, uint32_t x, uint32_t y);
OverscaledTileID(uint8_t z, uint32_t x, uint32_t y);
explicit OverscaledTileID(const CanonicalTileID&);
explicit OverscaledTileID(CanonicalTileID&&);
@@ -57,9 +57,10 @@ public:
bool isChildOf(const OverscaledTileID&) const;
uint32_t overscaleFactor() const;
OverscaledTileID scaledTo(uint8_t z) const;
- UnwrappedTileID unwrapTo(int16_t wrap) const;
+ UnwrappedTileID toUnwrapped() const;
const uint8_t overscaledZ;
+ const int16_t wrap;
const CanonicalTileID canonical;
};
@@ -137,40 +138,40 @@ inline std::array<CanonicalTileID, 4> CanonicalTileID::children() const {
} };
}
-inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, CanonicalTileID canonical_)
- : overscaledZ(overscaledZ_), canonical(std::move(canonical_)) {
+inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, CanonicalTileID canonical_)
+ : overscaledZ(overscaledZ_), wrap(wrap_), canonical(std::move(canonical_)) {
assert(overscaledZ >= canonical.z);
}
-inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, uint8_t z, uint32_t x, uint32_t y)
- : overscaledZ(overscaledZ_), canonical(z, x, y) {
+inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, uint8_t z, uint32_t x, uint32_t y)
+ : overscaledZ(overscaledZ_), wrap(wrap_), canonical(z, x, y) {
assert(overscaledZ >= canonical.z);
}
inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y)
- : overscaledZ(z), canonical(z, x, y) {
+ : overscaledZ(z), wrap(0), canonical(z, x, y) {
}
inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_)
- : overscaledZ(canonical_.z), canonical(canonical_) {
+ : overscaledZ(canonical_.z), wrap(0), canonical(canonical_) {
assert(overscaledZ >= canonical.z);
}
inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_)
- : overscaledZ(canonical_.z), canonical(std::forward<CanonicalTileID>(canonical_)) {
+ : overscaledZ(canonical_.z), wrap(0), canonical(std::forward<CanonicalTileID>(canonical_)) {
assert(overscaledZ >= canonical.z);
}
inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const {
- return overscaledZ == rhs.overscaledZ && canonical == rhs.canonical;
+ return overscaledZ == rhs.overscaledZ && wrap == rhs.wrap &&canonical == rhs.canonical;
}
inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const {
- return overscaledZ != rhs.overscaledZ || canonical != rhs.canonical;
+ return overscaledZ != rhs.overscaledZ || wrap != rhs.wrap || canonical != rhs.canonical;
}
inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const {
- return std::tie(overscaledZ, canonical) < std::tie(rhs.overscaledZ, rhs.canonical);
+ return std::tie(overscaledZ, wrap, canonical) < std::tie(rhs.overscaledZ, rhs.wrap, rhs.canonical);
}
inline uint32_t OverscaledTileID::overscaleFactor() const {
@@ -183,10 +184,10 @@ inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const {
}
inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const {
- return { z, z >= canonical.z ? canonical : canonical.scaledTo(z) };
+ return { z, wrap, z >= canonical.z ? canonical : canonical.scaledTo(z) };
}
-inline UnwrappedTileID OverscaledTileID::unwrapTo(int16_t wrap) const {
+inline UnwrappedTileID OverscaledTileID::toUnwrapped() const {
return { wrap, canonical };
}
@@ -232,7 +233,7 @@ inline std::array<UnwrappedTileID, 4> UnwrappedTileID::children() const {
inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const {
assert(overscaledZ >= canonical.z);
- return { overscaledZ, canonical };
+ return { overscaledZ, wrap, canonical };
}
inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const {
@@ -243,32 +244,19 @@ inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const fl
namespace std {
-template <> struct hash<mbgl::CanonicalTileID> {
- size_t operator()(const mbgl::CanonicalTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, id.x);
- boost::hash_combine(seed, id.y);
- boost::hash_combine(seed, id.z);
- return seed;
- }
+template <>
+struct hash<mbgl::CanonicalTileID> {
+ size_t operator()(const mbgl::CanonicalTileID& id) const;
};
-template <> struct hash<mbgl::UnwrappedTileID> {
- size_t operator()(const mbgl::UnwrappedTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
- boost::hash_combine(seed, id.wrap);
- return seed;
- }
+template <>
+struct hash<mbgl::UnwrappedTileID> {
+ size_t operator()(const mbgl::UnwrappedTileID& id) const;
};
-template <> struct hash<mbgl::OverscaledTileID> {
- size_t operator()(const mbgl::OverscaledTileID &id) const {
- std::size_t seed = 0;
- boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
- boost::hash_combine(seed, id.overscaledZ);
- return seed;
- }
+template <>
+struct hash<mbgl::OverscaledTileID> {
+ size_t operator()(const mbgl::OverscaledTileID& id) const;
};
} // namespace std
diff --git a/include/mbgl/tile/tile_necessity.hpp b/include/mbgl/tile/tile_necessity.hpp
new file mode 100644
index 0000000000..e51bf51d10
--- /dev/null
+++ b/include/mbgl/tile/tile_necessity.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+namespace mbgl {
+
+// Tiles can have two states: optional or required.
+// - optional means that only low-cost actions should be taken to obtain the data
+// (e.g. load from cache, but accept stale data)
+// - required means that every effort should be taken to obtain the data (e.g. load
+// from internet and keep the data fresh if it expires)
+enum class TileNecessity : bool {
+ Optional = false,
+ Required = true,
+};
+
+} // namespace mbgl
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/constants.hpp b/include/mbgl/util/constants.hpp
index 14aaa752bc..d5e55065c4 100644
--- a/include/mbgl/util/constants.hpp
+++ b/include/mbgl/util/constants.hpp
@@ -19,7 +19,8 @@ constexpr float tileSize = 512;
*
* Positions are stored as signed 16bit integers.
* One bit is lost for signedness to support features extending past the left edge of the tile.
- * One bit is lost because the line vertex buffer packs 1 bit of other data into the int.
+ * One bit is lost because the line vertex buffer used to pack 1 bit of other data into the int.
+ * This is no longer the case but we're reserving this bit anyway.
* One bit is lost to support features extending past the extent on the right edge of the tile.
* This leaves us with 2^13 = 8192
*/
@@ -37,6 +38,9 @@ constexpr double MIN_ZOOM = 0.0;
constexpr double MAX_ZOOM = 25.5;
constexpr float MIN_ZOOM_F = MIN_ZOOM;
constexpr float MAX_ZOOM_F = MAX_ZOOM;
+constexpr uint8_t DEFAULT_MAX_ZOOM = 22;
+
+constexpr uint8_t DEFAULT_PREFETCH_ZOOM_DELTA = 4;
constexpr uint64_t DEFAULT_MAX_CACHE_SIZE = 50 * 1024 * 1024;
@@ -57,7 +61,6 @@ extern const bool tileParseWarnings;
extern const bool styleParseWarnings;
extern const bool spriteWarnings;
extern const bool renderWarnings;
-extern const bool renderTree;
extern const bool labelTextMissingWarning;
extern const bool missingFontStackWarning;
extern const bool missingFontFaceWarning;
diff --git a/include/mbgl/util/convert.hpp b/include/mbgl/util/convert.hpp
index c2b3d9950d..02ec7feef9 100644
--- a/include/mbgl/util/convert.hpp
+++ b/include/mbgl/util/convert.hpp
@@ -1,3 +1,5 @@
+#include <mbgl/util/util.hpp>
+
#include <array>
#include <type_traits>
#include <utility>
@@ -7,8 +9,8 @@ namespace util {
template<typename To, typename From, std::size_t Size,
typename = std::enable_if_t<std::is_convertible<From, To>::value>>
-constexpr std::array<To, Size> convert(const std::array<From, Size>&from) {
- std::array<To, Size> to {};
+MBGL_CONSTEXPR std::array<To, Size> convert(const std::array<From, Size>&from) {
+ std::array<To, Size> to {{}};
std::copy(std::begin(from), std::end(from), std::begin(to));
return to;
}
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/geo.hpp b/include/mbgl/util/geo.hpp
index 6d725b102b..60043ee156 100644
--- a/include/mbgl/util/geo.hpp
+++ b/include/mbgl/util/geo.hpp
@@ -154,18 +154,84 @@ public:
sw.longitude() > ne.longitude();
}
- bool contains(const LatLng& point) const {
- return (point.latitude() >= sw.latitude() &&
- point.latitude() <= ne.latitude() &&
- point.longitude() >= sw.longitude() &&
- point.longitude() <= ne.longitude());
- }
-
- bool intersects(const LatLngBounds area) const {
- return (area.ne.latitude() > sw.latitude() &&
- area.sw.latitude() < ne.latitude() &&
- area.ne.longitude() > sw.longitude() &&
- area.sw.longitude() < ne.longitude());
+ bool crossesAntimeridian() const {
+ return (sw.wrapped().longitude() > ne.wrapped().longitude());
+ }
+
+ bool contains(const LatLng& point, LatLng::WrapMode wrap = LatLng::Unwrapped) const {
+ bool containsLatitude = point.latitude() >= sw.latitude() &&
+ point.latitude() <= ne.latitude();
+ if (!containsLatitude) {
+ return false;
+ }
+
+ bool containsUnwrappedLongitude = point.longitude() >= sw.longitude() &&
+ point.longitude() <= ne.longitude();
+ if (containsUnwrappedLongitude) {
+ return true;
+ } else if (wrap == LatLng::Wrapped) {
+ LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
+ auto ptLon = point.wrapped().longitude();
+ if (crossesAntimeridian()) {
+ return (ptLon >= wrapped.sw.longitude() &&
+ ptLon <= util::LONGITUDE_MAX) ||
+ (ptLon <= wrapped.ne.longitude() &&
+ ptLon >= -util::LONGITUDE_MAX);
+ } else {
+ return (ptLon >= wrapped.sw.longitude() &&
+ ptLon <= wrapped.ne.longitude());
+ }
+ }
+ return false;
+ }
+
+ bool contains(const LatLngBounds& area, LatLng::WrapMode wrap = LatLng::Unwrapped) const {
+ bool containsLatitude = area.north() <= north() && area.south() >= south();
+ if (!containsLatitude) {
+ return false;
+ }
+
+ bool containsUnwrapped = area.east() <= east() && area.west() >= west();
+ if(containsUnwrapped) {
+ return true;
+ } else if (wrap == LatLng::Wrapped) {
+ LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
+ LatLngBounds other(area.sw.wrapped(), area.ne.wrapped());
+ if (crossesAntimeridian() & !area.crossesAntimeridian()) {
+ return (other.east() <= util::LONGITUDE_MAX && other.west() >= wrapped.west()) ||
+ (other.east() <= wrapped.east() && other.west() >= -util::LONGITUDE_MAX);
+ } else {
+ return other.east() <= wrapped.east() && other.west() >= wrapped.west();
+ }
+ }
+ return false;
+ }
+
+ bool intersects(const LatLngBounds area, LatLng::WrapMode wrap = LatLng::Unwrapped) const {
+ bool latitudeIntersects = area.north() > south() && area.south() < north();
+ if (!latitudeIntersects) {
+ return false;
+ }
+
+ bool longitudeIntersects = area.east() > west() && area.west() < east();
+ if (longitudeIntersects) {
+ return true;
+ } else if (wrap == LatLng::Wrapped) {
+ LatLngBounds wrapped(sw.wrapped(), ne.wrapped());
+ LatLngBounds other(area.sw.wrapped(), area.ne.wrapped());
+ if (crossesAntimeridian()) {
+ return area.crossesAntimeridian() ||
+ other.east() > wrapped.west() ||
+ other.west() < wrapped.east();
+ } else if (other.crossesAntimeridian()){
+ return other.east() > wrapped.west() ||
+ other.west() < wrapped.east();
+ } else {
+ return other.east() > wrapped.west() &&
+ other.west() < wrapped.east();
+ }
+ }
+ return false;
}
private:
diff --git a/include/mbgl/util/geometry.hpp b/include/mbgl/util/geometry.hpp
index 6dc16bc514..a28c59a47d 100644
--- a/include/mbgl/util/geometry.hpp
+++ b/include/mbgl/util/geometry.hpp
@@ -2,6 +2,7 @@
#include <mapbox/geometry/geometry.hpp>
#include <mapbox/geometry/point_arithmetic.hpp>
+#include <mapbox/geometry/for_each_point.hpp>
namespace mbgl {
@@ -58,4 +59,9 @@ struct ToFeatureType {
FeatureType operator()(const mapbox::geometry::geometry_collection<T> &) const { return FeatureType::Unknown; }
};
+template <class T, typename F>
+auto forEachPoint(const Geometry<T>& g, F f) {
+ mapbox::geometry::for_each_point(g, f);
+}
+
} // namespace mbgl
diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp
index 91bf06d727..4887058f79 100644
--- a/include/mbgl/util/image.hpp
+++ b/include/mbgl/util/image.hpp
@@ -5,6 +5,7 @@
#include <mbgl/util/size.hpp>
#include <string>
+#include <cstring>
#include <memory>
#include <algorithm>
@@ -66,9 +67,9 @@ public:
template <typename T = Image>
T clone() const {
- T copy(size);
- std::copy(data.get(), data.get() + bytes(), copy.data.get());
- return copy;
+ T copy_(size);
+ std::copy(data.get(), data.get() + bytes(), copy_.data.get());
+ return copy_;
}
size_t stride() const { return channels * size.width; }
@@ -91,6 +92,31 @@ public:
operator=(std::move(newImage));
}
+ // Clears the rect area specified by `pt` and `size` from `dstImage`.
+ static void clear(Image& dstImg, const Point<uint32_t>& pt, const Size& size) {
+ if (size.isEmpty()) {
+ return;
+ }
+
+ if (!dstImg.valid()) {
+ throw std::invalid_argument("invalid destination for image clear");
+ }
+
+ if (size.width > dstImg.size.width ||
+ size.height > dstImg.size.height ||
+ pt.x > dstImg.size.width - size.width ||
+ pt.y > dstImg.size.height - size.height) {
+ throw std::out_of_range("out of range destination coordinates for image clear");
+ }
+
+ uint8_t* dstData = dstImg.data.get();
+
+ for (uint32_t y = 0; y < size.height; y++) {
+ const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels;
+ std::memset(dstData + dstOffset, 0, size.width * channels);
+ }
+ }
+
// Copy image data within `rect` from `src` to the rectangle of the same size at `pt`
// in `dst`. If the specified bounds exceed the bounds of the source or destination,
// throw `std::out_of_range`. Must not be used to move data within a single Image.
diff --git a/include/mbgl/util/indexed_tuple.hpp b/include/mbgl/util/indexed_tuple.hpp
index a414639530..fd0b931d36 100644
--- a/include/mbgl/util/indexed_tuple.hpp
+++ b/include/mbgl/util/indexed_tuple.hpp
@@ -31,16 +31,13 @@ public:
using std::tuple<Ts...>::tuple;
template <class I>
- static constexpr std::size_t Index = TypeIndex<I, Is...>::value;
-
- template <class I>
auto& get() {
- return std::get<Index<I>>(*this);
+ return std::get<TypeIndex<I, Is...>::value>(*this);
}
template <class I>
const auto& get() const {
- return std::get<Index<I>>(*this);
+ return std::get<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 a2103f18b2..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:
@@ -95,7 +126,13 @@ struct Interpolator<std::vector<T>>
: Uninterpolated {};
template <class T>
-constexpr bool Interpolatable = !std::is_base_of<Uninterpolated, Interpolator<T>>::value;
+struct Interpolatable
+ : std::conditional_t<
+ !std::is_base_of<Uninterpolated, Interpolator<T>>::value,
+ std::true_type,
+ std::false_type> {};
+
+
} // namespace util
} // namespace mbgl
diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp
index 3cc1146513..1613af3b36 100644
--- a/include/mbgl/util/projection.hpp
+++ b/include/mbgl/util/projection.hpp
@@ -75,10 +75,7 @@ public:
}
static Point<double> project(const LatLng& latLng, double scale) {
- return Point<double> {
- util::LONGITUDE_MAX + latLng.longitude(),
- util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX))
- } * worldSize(scale) / util::DEGREES_MAX;
+ return project_(latLng, worldSize(scale));
}
static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) {
@@ -89,6 +86,23 @@ 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 = ::round(std::min(pt.x, t2z));
+ auto y = ::round(std::min(t2z - pt.y, t2z));
+ return { x, y };
+ }
+private:
+ static Point<double> project_(const LatLng& latLng, double worldSize) {
+ return Point<double> {
+ util::LONGITUDE_MAX + latLng.longitude(),
+ util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX))
+ } * worldSize / util::DEGREES_MAX;
+ }
};
} // namespace mbgl
diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp
index 14352ca823..acbea80273 100644
--- a/include/mbgl/util/run_loop.hpp
+++ b/include/mbgl/util/run_loop.hpp
@@ -62,6 +62,12 @@ public:
push(task);
return std::make_unique<WorkRequest>(task);
}
+
+ void schedule(std::weak_ptr<Mailbox> mailbox) override {
+ invoke([mailbox] () {
+ Mailbox::maybeReceive(mailbox);
+ });
+ }
class Impl;
@@ -72,12 +78,6 @@ private:
void push(std::shared_ptr<WorkTask>);
- void schedule(std::weak_ptr<Mailbox> mailbox) override {
- invoke([mailbox] () {
- Mailbox::maybeReceive(mailbox);
- });
- }
-
void withMutex(std::function<void()>&& fn) {
std::lock_guard<std::mutex> lock(mutex);
fn();
diff --git a/include/mbgl/util/size.hpp b/include/mbgl/util/size.hpp
index 45c303969c..12c0ad056b 100644
--- a/include/mbgl/util/size.hpp
+++ b/include/mbgl/util/size.hpp
@@ -15,6 +15,10 @@ public:
constexpr uint32_t area() const {
return width * height;
}
+
+ constexpr float aspectRatio() const {
+ return static_cast<float>(width) / static_cast<float>(height);
+ }
constexpr bool isEmpty() const {
return width == 0 || height == 0;
diff --git a/include/mbgl/util/string.hpp b/include/mbgl/util/string.hpp
index de061647b5..13498ccb92 100644
--- a/include/mbgl/util/string.hpp
+++ b/include/mbgl/util/string.hpp
@@ -1,9 +1,38 @@
#pragma once
+#include <sstream>
#include <string>
#include <cassert>
+#include <cstdlib>
#include <exception>
+// Polyfill needed by Qt when building for Android with GCC
+#if defined(__ANDROID__) && defined(__GLIBCXX__)
+
+namespace std {
+
+template <typename T>
+std::string to_string(T value)
+{
+ std::ostringstream oss;
+ oss << value;
+
+ return oss.str();
+}
+
+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
+
namespace mbgl {
namespace util {
@@ -40,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/src/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp
index 572f46080e..672eebf6db 100644
--- a/src/mbgl/util/thread.hpp
+++ b/include/mbgl/util/thread.hpp
@@ -61,8 +61,6 @@ public:
}
~Thread() override {
- MBGL_VERIFY_THREAD(tid);
-
if (paused) {
resume();
}
diff --git a/include/mbgl/util/tileset.hpp b/include/mbgl/util/tileset.hpp
index 1256e9fe96..5a03e1a9da 100644
--- a/include/mbgl/util/tileset.hpp
+++ b/include/mbgl/util/tileset.hpp
@@ -1,7 +1,9 @@
#pragma once
#include <mbgl/util/range.hpp>
+#include <mbgl/util/constants.hpp>
+#include <tuple>
#include <vector>
#include <string>
#include <cstdint>
@@ -13,9 +15,18 @@ public:
enum class Scheme : bool { XYZ, TMS };
std::vector<std::string> tiles;
- Range<uint8_t> zoomRange { 0, 22 };
+ Range<uint8_t> zoomRange;
std::string attribution;
- Scheme scheme = Scheme::XYZ;
+ Scheme scheme;
+
+ 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)
+ : tiles(std::move(tiles_)),
+ zoomRange(std::move(zoomRange_)),
+ attribution(std::move(attribution_)),
+ scheme(scheme_) {}
// TileJSON also includes center, zoom, and bounds, but they are not used by mbgl.
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 3a4994917b..92f23d6718 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 {
@@ -34,11 +35,11 @@ struct UnitBezier {
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
constexpr UnitBezier(double p1x, double p1y, double p2x, double p2y)
: cx(3.0 * p1x)
- , bx(3.0 * (p2x - p1x) - cx)
- , ax(1.0 - cx - bx)
+ , bx(3.0 * (p2x - p1x) - (3.0 * p1x))
+ , ax(1.0 - (3.0 * p1x) - (3.0 * (p2x - p1x) - (3.0 * p1x)))
, cy(3.0 * p1y)
- , by(3.0 * (p2y - p1y) - cy)
- , ay(1.0 - cy - by) {
+ , by(3.0 * (p2y - p1y) - (3.0 * p1y))
+ , ay(1.0 - (3.0 * p1y) - (3.0 * (p2y - p1y) - (3.0 * p1y))) {
}
double sampleCurveX(double t) const {
@@ -102,6 +103,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/include/mbgl/util/util.hpp b/include/mbgl/util/util.hpp
index c5a7cb3780..7960b40299 100644
--- a/include/mbgl/util/util.hpp
+++ b/include/mbgl/util/util.hpp
@@ -12,3 +12,10 @@
#define MBGL_VERIFY_THREAD(tid)
#endif
+
+// GCC 4.9 compatibility
+#if !defined(__GNUC__) || __GNUC__ >= 5
+#define MBGL_CONSTEXPR constexpr
+#else
+#define MBGL_CONSTEXPR inline
+#endif
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject 09bec658e3f8de18a7964f5633aea9716dcb391
+Subproject 2e6fc8901ec3937a0e71da40dff9d7d037dee74
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..6fd701b72c 100644
--- a/common/ca-bundle.crt
+++ b/misc/ca-bundle.crt
@@ -1,7 +1,7 @@
##
## Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Fri Jan 27 10:57:10 2017 GMT
+## Certificate data from Mozilla as of: Wed Dec 13 12:22:38 2017 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: 2b2dbe5244e0047e088c597998883a913f6c5fffd1cb5c0fe5a368c8466cb2ec
##
@@ -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-----
@@ -419,56 +326,6 @@ Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
-----END CERTIFICATE-----
-Comodo Secure Services root
-===========================
------BEGIN CERTIFICATE-----
-MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
-R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
-TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw
-MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu
-Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi
-BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP
-9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc
-rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC
-oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V
-p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E
-FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
-gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj
-YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm
-aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm
-4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
-Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL
-DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw
-pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H
-RR3B7Hzs/Sk=
------END CERTIFICATE-----
-
-Comodo Trusted Services root
-============================
------BEGIN CERTIFICATE-----
-MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
-R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
-TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw
-MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h
-bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw
-IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7
-3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y
-/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6
-juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS
-ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud
-DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
-/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp
-ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl
-cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw
-uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
-pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA
-BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l
-R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O
-9y5Xt5hwXsjEeLBi
------END CERTIFICATE-----
-
QuoVadis Root CA
================
-----BEGIN CERTIFICATE-----
@@ -608,32 +465,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-----
@@ -831,38 +662,6 @@ CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
+fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
-----END CERTIFICATE-----
-Swisscom Root CA 1
-==================
------BEGIN CERTIFICATE-----
-MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG
-EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
-dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4
-MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
-aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC
-IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM
-MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF
-NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe
-AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC
-b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn
-7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN
-cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp
-WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5
-haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY
-MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
-HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
-BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9
-MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn
-jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ
-MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H
-VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl
-vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl
-OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3
-1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq
-nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy
-x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW
-NY6E0F/6MBr1mmz0DlP5OlvRHA==
------END CERTIFICATE-----
-
DigiCert Assured ID Root CA
===========================
-----BEGIN CERTIFICATE-----
@@ -1220,33 +1019,6 @@ wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----
-WellsSecure Public Root Certificate Authority
-=============================================
------BEGIN CERTIFICATE-----
-MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
-F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
-NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
-MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
-bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
-VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
-iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
-i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
-bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
-K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
-AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
-cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
-lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
-i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
-GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
-Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
-K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
-bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
-qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
-E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
-tylv2G0xffX8oRAHh84vWdw+WNs=
------END CERTIFICATE-----
-
COMODO ECC Certification Authority
==================================
-----BEGIN CERTIFICATE-----
@@ -1308,46 +1080,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-----
@@ -1493,49 +1225,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-----
@@ -2630,93 +2319,6 @@ 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-----
@@ -3538,30 +3140,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-----
@@ -4041,3 +3619,28 @@ 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-----
diff --git a/cloudformation/travis.template b/misc/cloudformation.template
index 29f2402b20..db4e59eb7e 100644
--- a/cloudformation/travis.template
+++ b/misc/cloudformation.template
@@ -130,22 +130,7 @@
}
]
}
- }
- ]
- }
- },
- "BuildUserKey": {
- "Type": "AWS::IAM::AccessKey",
- "Properties": {
- "UserName": {
- "Ref": "BuildUser"
- }
- }
- },
- "BitriseUser": {
- "Type": "AWS::IAM::User",
- "Properties": {
- "Policies": [
+ },
{
"PolicyName": "get-signing-key",
"PolicyDocument": {
@@ -223,11 +208,11 @@
]
}
},
- "BitriseUserKey": {
+ "BuildUserKey": {
"Type": "AWS::IAM::AccessKey",
"Properties": {
"UserName": {
- "Ref": "BitriseUser"
+ "Ref": "BuildUser"
}
}
},
@@ -312,19 +297,6 @@
]
}
},
- "BitriseAccessKeyId": {
- "Value": {
- "Ref": "BitriseUserKey"
- }
- },
- "BitriseSecretAccessKey": {
- "Value": {
- "Fn::GetAtt": [
- "BitriseUserKey",
- "SecretAccessKey"
- ]
- }
- },
"AndroidInstanceProfile": {
"Value": {
"Fn::GetAtt": [
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 e428a0db43..4ff4b32735 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@mapbox/mapbox-gl-native",
- "version": "3.5.4",
+ "version": "3.5.8",
"description": "Renders map tiles with Mapbox GL",
"keywords": [
"mapbox",
@@ -13,15 +13,19 @@
},
"license": "BSD-2-Clause",
"dependencies": {
- "node-pre-gyp": "^0.6.28",
- "nan": "^2.4.0"
+ "nan": "^2.6.2",
+ "node-pre-gyp": "^0.6.37",
+ "npm-run-all": "^4.0.2"
},
"devDependencies": {
"aws-sdk": "^2.3.5",
"csscolorparser": "^1.0.2",
"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",
"pngjs": "^3.0.0",
"request": "^2.72.0",
@@ -35,7 +39,10 @@
"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": "node platform/node/test/render.test.js && node platform/node/test/query.test.js"
+ "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"
},
"gypfile": true,
"binary": {
diff --git a/platform/android/.gitignore b/platform/android/.gitignore
index 7a3db0aafd..4abd458378 100644
--- a/platform/android/.gitignore
+++ b/platform/android/.gitignore
@@ -20,7 +20,6 @@ local.properties
# Token file
MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml
-MapboxGLAndroidSDKWearTestApp/src/main/res/values/developer-config.xml
# Twitter Fabric / Crashlytics
fabric.properties
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md
index d8a58fe140..213676c8be 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -1,13 +1,208 @@
-# Changelog for Mapbox Android SDK
+# Changelog for the Mapbox Maps SDK for Android
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
-## master
-* Add support for ImageSource [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
-
-## 5.1.0 - TBA
+## 5.2.0 - TBA
+
+## 5.2.0-beta.4 - November 3, 2017
+
+- Revert adding mapbox-android-core dependency (#10354) [#10380](https://github.com/mapbox/mapbox-gl-native/pull/10380)
+- Asynchronous TextureView [#10370](https://github.com/mapbox/mapbox-gl-native/pull/10370)
+- Workaround OkHttp bug on Android O [10366](https://github.com/mapbox/mapbox-gl-native/pull/10366)
+- Revisit logo resize [10553](https://github.com/mapbox/mapbox-gl-native/pull/10353)
+- Logo resize for MapSnapshotter [#10312](https://github.com/mapbox/mapbox-gl-native/pull/10312)
+- Make location provider optional [#10354](https://github.com/mapbox/mapbox-gl-native/pull/10354)
+- Check for positive animation value [#10348](https://github.com/mapbox/mapbox-gl-native/pull/10348)
+- Fix IAE of ease/animate [#10338](https://github.com/mapbox/mapbox-gl-native/pull/10338)
+- Run full test suite on CI [#10333](https://github.com/mapbox/mapbox-gl-native/pull/10333)
+- Make sure camera position gets updated in onFinish() callback after camera.move [#10324](https://github.com/mapbox/mapbox-gl-native/pull/10324)
+- throw IAE in animate() and easeCamera() when duration <= 0 [#10321](https://github.com/mapbox/mapbox-gl-native/pull/10321)
+- Don't save state if map hasn't been initialised [#10320](https://github.com/mapbox/mapbox-gl-native/pull/10320)
+- Make map snapshot optional [#10310](https://github.com/mapbox/mapbox-gl-native/pull/10310)
+- Synchronise locationlastions with Transifex [#10309](https://github.com/mapbox/mapbox-gl-native/pull/10309)
+- MapboxMap#addImages [#10281](https://github.com/mapbox/mapbox-gl-native/pull/10281)
+- Move shape annotation click handling to core [#10267](https://github.com/mapbox/mapbox-gl-native/pull/10267)
+- Map snapshotter additions [#10163](https://github.com/mapbox/mapbox-gl-native/pull/10163)
+- Add velocity to gestures / port animations to SDK animators [#10202](https://github.com/mapbox/mapbox-gl-native/pull/10202)
+- Don't save state if map hasn't been initialised [#10320](https://github.com/mapbox/mapbox-gl-native/pull/10320)
+- android.hardware.location.gps feature should not be required [#10347](https://github.com/mapbox/mapbox-gl-native/pull/10347)
+
+## 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)
+
+## 5.2.0-beta.2 - October 19, 2017
+
+- Wire up MapZoomButtonController with camera change events [#10221](https://github.com/mapbox/mapbox-gl-native/pull/10221)
+- Execute callbacks only when not idle [#10220](https://github.com/mapbox/mapbox-gl-native/pull/10220)
+- Cleanup unused gradle plugins [#10211](https://github.com/mapbox/mapbox-gl-native/pull/10211)
+- add FileSource pause/resume [#9977](https://github.com/mapbox/mapbox-gl-native/pull/9977)
+- add make target for ndk-stack [#10185](https://github.com/mapbox/mapbox-gl-native/pull/10185)
+- Add interpolator examples [#10067](https://github.com/mapbox/mapbox-gl-native/pull/10067)
+- 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)
+
+## 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)
+- 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)
+- API for platform side animations [#10001](https://github.com/mapbox/mapbox-gl-native/pull/10001)
+- Android asynchronous rendering [#9576](https://github.com/mapbox/mapbox-gl-native/pull/9576)
+- Set error handler when starting snapshotter [#10035](https://github.com/mapbox/mapbox-gl-native/pull/10035)
+- Hook camera events into compass [#10019](https://github.com/mapbox/mapbox-gl-native/pull/10019)
+- Testapp cleanup [#10006](https://github.com/mapbox/mapbox-gl-native/pull/10006)
+- Update zoom function example with selected state [#9987](https://github.com/mapbox/mapbox-gl-native/pull/9987)
+- Add style inspection to debug activity [#9773](https://github.com/mapbox/mapbox-gl-native/pull/9773)
+- Bump external dependencies [#9972](https://github.com/mapbox/mapbox-gl-native/pull/9972)
+- Don't recycle bitmap for icon reuse. [#9966](https://github.com/mapbox/mapbox-gl-native/pull/9966)
+- Android snapshotter [#9748](https://github.com/mapbox/mapbox-gl-native/pull/9748)
+- Revert #9764 [#9851](https://github.com/mapbox/mapbox-gl-native/pull/9851)
+- Update docs replacing Bitrise mentions with CircleCI [#9515](https://github.com/mapbox/mapbox-gl-native/pull/9515)
+- Style image accessor [#9763](https://github.com/mapbox/mapbox-gl-native/pull/9763)
+- Update readme with checkstyle and ndk-stack [#9788](https://github.com/mapbox/mapbox-gl-native/pull/9788)
+- make android-check [#9787](https://github.com/mapbox/mapbox-gl-native/pull/9787)
+- Deprecate MyLocationView in favor of LocationLayer plugin [#9771](https://github.com/mapbox/mapbox-gl-native/pull/9771)
+- Increase firebase timeout for CI testing [#9774](https://github.com/mapbox/mapbox-gl-native/pull/9774)
+- Restore max zoom to 25.5 [#9765](https://github.com/mapbox/mapbox-gl-native/pull/9765)
+- Update example of camera zoom function on a symbol layer. [#9743](https://github.com/mapbox/mapbox-gl-native/pull/9743)
+- Optimise icon management [#9643](https://github.com/mapbox/mapbox-gl-native/pull/9643)
+- Expose setStyleJson and getStyleJson [#9714](https://github.com/mapbox/mapbox-gl-native/pull/9714)
+- update LatLngBounds activity with BottomSheet interaction [#9736](https://github.com/mapbox/mapbox-gl-native/pull/9736)
+- post updating InfoWindow update for InfoWindowAdapter [#9716](https://github.com/mapbox/mapbox-gl-native/pull/9716)
+- Annotate MapboxMap class with UiThread [#9712](https://github.com/mapbox/mapbox-gl-native/pull/9712)
+- Move ZoomButtonController creation to view initalisation [#9587](https://github.com/mapbox/mapbox-gl-native/pull/9587)
+- Solve lint issues, reduce baseline [#9627](https://github.com/mapbox/mapbox-gl-native/pull/9627)
+- Remove wear module from project [#9618](https://github.com/mapbox/mapbox-gl-native/pull/9618)
+- Add zMediaOverlay configuration + bottom sheet integration [#9592](https://github.com/mapbox/mapbox-gl-native/pull/9592)
+- Forward getMapAsync to map for fragment [#9621](https://github.com/mapbox/mapbox-gl-native/pull/9621)
+- Make target for dumping system gfx information [#9616](https://github.com/mapbox/mapbox-gl-native/pull/9616)
+- Make target documentation [#9617](https://github.com/mapbox/mapbox-gl-native/pull/9617)
+- onGlobalLayout hook for map creation [#9607](https://github.com/mapbox/mapbox-gl-native/pull/9607)
+- Custom viewpager for horizontal swiping [#9601](https://github.com/mapbox/mapbox-gl-native/pull/9601)
+- Disable program caching on Adreno 3xx, 4xx, and 5xx GPUs due to known bugs [#9574](https://github.com/mapbox/mapbox-gl-native/pull/9574)
+- Avoid creating InfoWindow iterator if no InfoWindows are shown [#9477](https://github.com/mapbox/mapbox-gl-native/pull/9477)
+- Rewire map initialisation [#9462](https://github.com/mapbox/mapbox-gl-native/pull/9462)
+- Trying to update non-existent polyline fix [#9544](https://github.com/mapbox/mapbox-gl-native/pull/9544)
+- Location accuracy threshold [#9472](https://github.com/mapbox/mapbox-gl-native/pull/9472)
+- Rewire gesture handling and telemetry event push [#9494](https://github.com/mapbox/mapbox-gl-native/pull/9494)
+- run style instrumentation tests on CI [#9353](https://github.com/mapbox/mapbox-gl-native/pull/9353)
+- 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)
+
+## 5.1.5 - October 31, 2017
+
+* Remove obsolete terminate context/display calls [#10162](https://github.com/mapbox/mapbox-gl-native/pull/10162)
+* Determine need for clip ID based on actual layers/tiles [#10216](https://github.com/mapbox/mapbox-gl-native/pull/10216)
+* Correctly alter sprite URLs [#10217](https://github.com/mapbox/mapbox-gl-native/pull/10217)
+* Russian and Ukrainian localizations [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
+
+## 5.1.4 - September 25, 2017
+
+* Update translations [#10033](https://github.com/mapbox/mapbox-gl-native/pull/10033) & [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
+* Continue rendering tiles despite erros [#10012](https://github.com/mapbox/mapbox-gl-native/pull/10012)
+* Fix layer z-fighting [#9942](https://github.com/mapbox/mapbox-gl-native/pull/9942)
+* Align line vertex to 4-byte boundary [#9943](https://github.com/mapbox/mapbox-gl-native/pull/9943)
+* Bump proguard config for OkHttp [#9970](https://github.com/mapbox/mapbox-gl-native/pull/9970)
+* Remove database on schema downgrade [#9837](https://github.com/mapbox/mapbox-gl-native/pull/9837)
+* Disable rotation gesture when pinch zooming [#10026](https://github.com/mapbox/mapbox-gl-native/pull/10026)
+* Do not check if connection is local request [#9968](https://github.com/mapbox/mapbox-gl-native/pull/9968)
+* Harden offline region deletion [#9967](https://github.com/mapbox/mapbox-gl-native/pull/9967)
+* Clear out mapCallback's OnMapReadyListeners on destroy [#9957](https://github.com/mapbox/mapbox-gl-native/pull/9957)
+* Avoid adding duplicate points to bounds [#9955](https://github.com/mapbox/mapbox-gl-native/pull/9955)
+* Download is complete fix [#9913](https://github.com/mapbox/mapbox-gl-native/pull/9913)
+* MAS 2.2.3 [#9901](https://github.com/mapbox/mapbox-gl-native/pull/9901)
+* Russian and Ukrainian localizations [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945)
+
+## 5.1.3 - August 18, 2017
+
+* Use separate attribute component for line normals [#9753](https://github.com/mapbox/mapbox-gl-native/pull/9753)
+* Track state of initial overlain views margins [#9391](https://github.com/mapbox/mapbox-gl-native/pull/9391)
+* Compability for Samsung devices forcing 3-4 array vector length [#9746](https://github.com/mapbox/mapbox-gl-native/pull/9746)
+* Smallest LatLngBounds when visible region crosses dateline [#9747](https://github.com/mapbox/mapbox-gl-native/pull/9747)
+* Readd ProjectMeters [#9766](https://github.com/mapbox/mapbox-gl-native/pull/9766)
+* Enable location change animation of MyLocationView by default [#9779](https://github.com/mapbox/mapbox-gl-native/pull/9779)
+* Avoid IndexOutOfBoundsException when destroying map object [#9789](https://github.com/mapbox/mapbox-gl-native/pull/9789)
+* MAS 2.2.1 [#9796](https://github.com/mapbox/mapbox-gl-native/pull/9796)
+
+## 5.1.2 - August 2, 2017
+
+* Disable program caching on Ardreno GPU 3xx, 4xx and 5xx [#9625](https://github.com/mapbox/mapbox-gl-native/pull/9625)
+* GeoJSON proguard issue [#9577](https://github.com/mapbox/mapbox-gl-native/pull/9577)
+* Harden click events of shape annotations [#9585](https://github.com/mapbox/mapbox-gl-native/pull/9585)
+* Validate Marker before opening InfoWindow [#9586](https://github.com/mapbox/mapbox-gl-native/pull/9586)
+* Fix added to map check [#9602](https://github.com/mapbox/mapbox-gl-native/pull/9602)
+* Don't query shape annotations if none were added [#9606](https://github.com/mapbox/mapbox-gl-native/pull/9606)
+* Fix compass direction [#9632](https://github.com/mapbox/mapbox-gl-native/pull/9632)
+* Remove preview image integration [#9657](https://github.com/mapbox/mapbox-gl-native/pull/9657)
+* Try/catch initialization of telemetry [#9658](https://github.com/mapbox/mapbox-gl-native/pull/9658)
+* Fix typo OnCameraMoveStartedListener [#9664](https://github.com/mapbox/mapbox-gl-native/pull/9664)
+* Bump MAS dependency to 5.2.0 [#9671](https://github.com/mapbox/mapbox-gl-native/pull/9671)
+
+## 5.1.1 - July 21, 2017
+
+* Rework attribution binding [#9433](https://github.com/mapbox/mapbox-gl-native/pull/9433)
+* BackendScope changes [#9538](https://github.com/mapbox/mapbox-gl-native/pull/9538)
+* Invisible MarkerView performance fix [#9420](https://github.com/mapbox/mapbox-gl-native/pull/9420)
+* Polyline/Polygon click listeners [#9443](https://github.com/mapbox/mapbox-gl-native/pull/9443)
+* Hit test Marker/MarkerViews [#9424](https://github.com/mapbox/mapbox-gl-native/pull/9424)
+* Fine tune fling gesture [#9532](https://github.com/mapbox/mapbox-gl-native/pull/9532)
+* Bump OkHttp 3.8.0, Android 7.x crashes [#9522](https://github.com/mapbox/mapbox-gl-native/pull/9522)
+* MyLocationView tint fix [#9410](https://github.com/mapbox/mapbox-gl-native/pull/9410)
+* VisibleRegion logic [#9428](https://github.com/mapbox/mapbox-gl-native/pull/9428)
+* Validate motion event [#9434](https://github.com/mapbox/mapbox-gl-native/pull/9434)
+* Add French localization [#9545](https://github.com/mapbox/mapbox-gl-native/pull/9545)
+* Fix public.xml [#9525](https://github.com/mapbox/mapbox-gl-native/pull/9525/files)
+* Manage InfoWindow selection in AnnotationManager [#9567](https://github.com/mapbox/mapbox-gl-native/pull/9567)
+* Increase touch target size [#9565](https://github.com/mapbox/mapbox-gl-native/pull/9565)
+* Work around Adreno 2xx GPU bugs [#9573](https://github.com/mapbox/mapbox-gl-native/pull/9573)
+
+## 5.1.0 - June 30, 2017
+
+* Update to MAS 2.1.3 [#9402](https://github.com/mapbox/mapbox-gl-native/pull/9402)
+* Downgrade LOST to v1.1.1 [#9394](https://github.com/mapbox/mapbox-gl-native/pull/9394)
+* OnCameraIdle hook into quickzoom gesture [#9339](https://github.com/mapbox/mapbox-gl-native/pull/9339)
+* LatLngBounds conversion regression, add test [#9324](https://github.com/mapbox/mapbox-gl-native/pull/9324)
+
+## 5.1.0-beta.5 - June 21, 2017
+
+* Update MAS dependency to 2.1.2 [#9311](https://github.com/mapbox/mapbox-gl-native/pull/9311)
+* Update LOST dependency to 3.0.1 [#9302](https://github.com/mapbox/mapbox-gl-native/pull/9302)
+* Fix Pulse example Parcelable creator [#9283](https://github.com/mapbox/mapbox-gl-native/pull/9283)
+* Custom marker view anchoring [#9282](https://github.com/mapbox/mapbox-gl-native/pull/9282)
+* Update Activity test generation [#9276](https://github.com/mapbox/mapbox-gl-native/pull/9276)
+* Validate camera position before transforming [#9275](https://github.com/mapbox/mapbox-gl-native/pull/9275)
+* Revisit javadoc [#9266](https://github.com/mapbox/mapbox-gl-native/pull/9266)
+* Build with NDK 15 [#9263](https://github.com/mapbox/mapbox-gl-native/pull/9263)
+* Snapshot with view content [#9263](https://github.com/mapbox/mapbox-gl-native/pull/9263)
+* Update source changed javadoc [#9243](https://github.com/mapbox/mapbox-gl-native/pull/9243)
+* Run tests on UI-thread [#9198](https://github.com/mapbox/mapbox-gl-native/pull/9198)
+* Fix trackball on worker thread [#9305](https://github.com/mapbox/mapbox-gl-native/pull/9305)
+
+## 5.1.0-beta.4 - June 9, 2017
+
+* Option to disable location change animation [#9210](https://github.com/mapbox/mapbox-gl-native/pull/9210)
+* Invalidating MyLocationView bearing when not following [#9212](https://github.com/mapbox/mapbox-gl-native/pull/9212)
+* Remove upgrade runtime exceptions [#9191](https://github.com/mapbox/mapbox-gl-native/pull/9191)
+* Check source usage before removal [#9129](https://github.com/mapbox/mapbox-gl-native/pull/9129)
* Fix tracking mode + camera race condition [#9133](https://github.com/mapbox/mapbox-gl-native/pull/9133)
* Harden orientation changes [#9128](https://github.com/mapbox/mapbox-gl-native/pull/9128)
+* 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)
+* 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)
## 5.1.0-beta.3 - May 26, 2017
@@ -407,12 +602,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/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore
new file mode 100644
index 0000000000..cec211fe81
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/.gitignore
@@ -0,0 +1,2 @@
+lint-baseline.xml
+lint/lint-baseline-local.xml \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index adbc7cad7f..32eeab138f 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -1,24 +1,19 @@
apply plugin: 'com.android.library'
dependencies {
- compile rootProject.ext.dep.supportAnnotations
- compile rootProject.ext.dep.supportV4
- compile rootProject.ext.dep.supportDesign
- compile rootProject.ext.dep.timber
- compile rootProject.ext.dep.okhttp3
- compile rootProject.ext.dep.lost
- testCompile rootProject.ext.dep.junit
- testCompile rootProject.ext.dep.mockito
-
- // Mapbox Android Services (GeoJSON support)
- compile(rootProject.ext.dep.mapboxJavaGeoJSON) {
- transitive = true
- }
-
- // Mapbox Android Services (Telemetry support)
- compile(rootProject.ext.dep.mapboxAndroidTelemetry) {
- transitive = true
+ api rootProject.ext.dep.mapboxAndroidTelemetry
+ api rootProject.ext.dep.mapboxJavaGeoJSON
+ implementation rootProject.ext.dep.supportAnnotations
+ implementation rootProject.ext.dep.supportFragmentV4
+ implementation rootProject.ext.dep.timber
+ implementation rootProject.ext.dep.okhttp3
+ compileOnly(rootProject.ext.dep.lost) {
+ exclude group: 'com.google.guava'
+ exclude group: 'com.android.support'
}
+ testImplementation rootProject.ext.dep.lost
+ testImplementation rootProject.ext.dep.junit
+ testImplementation rootProject.ext.dep.mockito
}
android {
@@ -39,11 +34,14 @@ android {
// to invoke the Java tests. When we explicitly specify an ABI of 'none', no native dependencies are
// added. When another ABI is specified explicitly, we're just going to build that ABI. In all other
// cases, all ABIs are built.
- // When invoking from the command line, set `-Pmapbox.abis=...` to only build the desired architectures.
+ //
+ // When invoking from the command line or to override the device default, set `-Pmapbox.abis=...` to
+ // only build the desired architectures.
+ //
// When building from Android Studio, gradle.properties sets `android.buildOnlyTargetAbi=true` so that
// only the architecture for the device you're running on gets built.
def abi = 'all'
- if (!project.hasProperty('android.injected.invoked.from.ide')) {
+ if (!project.hasProperty('android.injected.invoked.from.ide') || project.hasProperty("mapbox.abis")) {
// Errors when the user invokes Gradle from the command line and didn't set mapbox.abis
abi = project.getProperty("mapbox.abis")
}
@@ -114,8 +112,10 @@ android {
}
lintOptions {
+ disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission'
+ baseline file("lint-baseline-local.xml")
checkAllWarnings true
- warningsAsErrors true
+ warningsAsErrors false
}
testOptions {
@@ -150,3 +150,4 @@ 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'
diff --git a/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle b/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle
index e0bc076d3d..420ccb473a 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle
+++ b/platform/android/MapboxGLAndroidSDK/gradle-checkstyle.gradle
@@ -16,6 +16,7 @@ task checkstyle(type: Checkstyle) {
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/MapboxGLAndroidSDK/gradle-javadoc.gradle b/platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle
index eeb5e9b4d6..39372b4378 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle
+++ b/platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle
@@ -10,7 +10,7 @@ android.libraryVariants.all { variant ->
options.windowTitle("Mapbox Android SDK $VERSION_NAME Reference")
options.docTitle("Mapbox Android SDK $VERSION_NAME")
options.header("Mapbox Android SDK $VERSION_NAME Reference")
- options.bottom("&copy; 2015&ndash;2017 Mapbox. All rights reserved.")
+ options.bottom("&copy; 2015&ndash;2018 Mapbox. All rights reserved.")
options.links("http://docs.oracle.com/javase/7/docs/api/")
options.linksOffline("http://d.android.com/reference/", "$System.env.ANDROID_HOME/docs/reference")
options.overview("src/main/java/overview.html")
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index 9f555da5f8..f00eb1d77f 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -1,5 +1,5 @@
GROUP=com.mapbox.mapboxsdk
-VERSION_NAME=5.1.0-SNAPSHOT
+VERSION_NAME=6.0.0-SNAPSHOT
POM_DESCRIPTION=Mapbox GL Android SDK
POM_URL=https://github.com/mapbox/mapbox-gl-native
@@ -11,10 +11,11 @@ POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=mapbox
POM_DEVELOPER_NAME=Mapbox
-POM_NAME=Mapbox Android SDK
+POM_NAME=Mapbox Maps SDK for Android
POM_ARTIFACT_ID=mapbox-android-sdk
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
new file mode 100644
index 0000000000..0a76f53505
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml
@@ -0,0 +1,37 @@
+<?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
new file mode 100644
index 0000000000..fd65c9f627
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml
@@ -0,0 +1,44 @@
+<?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 8e47815451..b5a1d82c81 100644
--- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
+++ b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
@@ -6,4 +6,10 @@
-keep class com.mapbox.mapboxsdk.** { *; }
-keep interface com.mapbox.mapboxsdk.** { *; }
-keep class com.mapbox.services.android.telemetry.** { *; }
--keep class com.mapbox.services.commons.** { *;} \ No newline at end of file
+-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 \ 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 231e36e092..b61035a008 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
<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-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
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
index 71fb9aa168..db492b6556 100644
--- 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
@@ -54,8 +54,7 @@ public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
ViewConfiguration config = ViewConfiguration.get(context);
- // We divide edge slop by 2 to make rotation gesture happen more easily #6870
- edgeSlop = config.getScaledEdgeSlop() / 2;
+ edgeSlop = config.getScaledEdgeSlop();
}
@Override
@@ -222,4 +221,4 @@ public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
return focus.y;
}
-}
+} \ No newline at end of file
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
new file mode 100644
index 0000000000..8ea7e61eee
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java
@@ -0,0 +1,107 @@
+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 8a75176ccd..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
@@ -1,15 +1,48 @@
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() {
- System.loadLibrary("mapbox-gl");
+ try {
+ 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 bc1049d146..b67b6e96f2 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
@@ -1,6 +1,6 @@
package com.mapbox.mapboxsdk;
-import android.app.Application;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -15,22 +15,27 @@ import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
import com.mapbox.services.android.telemetry.MapboxTelemetry;
import com.mapbox.services.android.telemetry.location.LocationEngine;
import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
+import com.mapbox.services.android.telemetry.location.LocationEngineProvider;
+
+import timber.log.Timber;
/**
- * The entry point of the Mapbox Android SDK.
+ * The entry point to initialize the Mapbox Android SDK.
* <p>
* Obtain a reference by calling {@link #getInstance(Context, String)}. Usually this class is configured in
- * {@link Application#onCreate()} and is responsible for the active access token, application context, and
+ * Application#onCreate() and is responsible for the active access token, application context, and
* connectivity state.
* </p>
*/
@UiThread
public final class Mapbox {
+ @SuppressLint("StaticFieldLeak")
private static Mapbox INSTANCE;
private Context context;
private String accessToken;
private Boolean connected;
+ private LocationEngine locationEngine;
/**
* Get an instance of Mapbox.
@@ -46,19 +51,27 @@ public final class Mapbox {
public static synchronized Mapbox getInstance(@NonNull Context context, @NonNull String accessToken) {
if (INSTANCE == null) {
Context appContext = context.getApplicationContext();
- INSTANCE = new Mapbox(appContext, accessToken);
- LocationEngine locationEngine = LocationSource.getLocationEngine(appContext);
+ LocationEngineProvider locationEngineProvider = new LocationEngineProvider(context);
+ LocationEngine locationEngine = locationEngineProvider.obtainBestLocationEngineAvailable();
+ INSTANCE = new Mapbox(appContext, accessToken, locationEngine);
locationEngine.setPriority(LocationEnginePriority.NO_POWER);
- MapboxTelemetry.getInstance().initialize(
- appContext, accessToken, BuildConfig.MAPBOX_EVENTS_USER_AGENT, locationEngine);
+
+ try {
+ MapboxTelemetry.getInstance().initialize(
+ appContext, accessToken, BuildConfig.MAPBOX_EVENTS_USER_AGENT, locationEngine);
+ } catch (Exception exception) {
+ Timber.e(exception, "Unable to instantiate Mapbox telemetry");
+ }
+
ConnectivityReceiver.instance(appContext);
}
return INSTANCE;
}
- Mapbox(@NonNull Context context, @NonNull String accessToken) {
+ Mapbox(@NonNull Context context, @NonNull String accessToken, LocationEngine locationEngine) {
this.context = context;
this.accessToken = accessToken;
+ this.locationEngine = locationEngine;
}
/**
@@ -96,6 +109,8 @@ public final class Mapbox {
/**
* Application context
+ *
+ * @return the application context
*/
public static Context getApplicationContext() {
return INSTANCE.context;
@@ -129,4 +144,25 @@ 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/annotations/BaseMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
index ddedf3debf..3fd2fa4ebf 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
@@ -14,7 +14,10 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
*
* @param <U> Type of the marker view to be composed.
* @param <T> Type of the builder to be used for composing.
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+@Deprecated
public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends BaseMarkerViewOptions<U, T>>
implements Parcelable {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java
index 07e038c08c..c58cc310a8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java
@@ -27,14 +27,32 @@ public class BubbleLayout extends LinearLayout {
private float strokeWidth;
private int strokeColor;
+ /**
+ * Creates an instance of bubble layout.
+ *
+ * @param context The context used to inflate this bubble layout
+ */
public BubbleLayout(Context context) {
this(context, null, 0);
}
+ /**
+ * Creates an instance of bubble layout.
+ *
+ * @param context The context used to inflate this bubble layout
+ * @param attrs The attribute set to initialise this bubble layout from
+ */
public BubbleLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
+ /**
+ * Creates an instance of bubble layout.
+ *
+ * @param context The context used to inflate this bubble layout
+ * @param attrs The attribute set to initialise this bubble layout from
+ * @param defStyleAttr The default style to apply this bubble layout with
+ */
public BubbleLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -78,10 +96,21 @@ public class BubbleLayout extends LinearLayout {
return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
+ /**
+ * Get the arrow direction.
+ *
+ * @return the arrow direction
+ */
public ArrowDirection getArrowDirection() {
return arrowDirection;
}
+ /**
+ * Set the arrow direction.
+ *
+ * @param arrowDirection The direction of the arrow
+ * @return this
+ */
public BubbleLayout setArrowDirection(ArrowDirection arrowDirection) {
resetPadding();
this.arrowDirection = arrowDirection;
@@ -89,10 +118,21 @@ public class BubbleLayout extends LinearLayout {
return this;
}
+ /**
+ * Get the arrow width.
+ *
+ * @return the width of the arrow
+ */
public float getArrowWidth() {
return arrowWidth;
}
+ /**
+ * Set the arrow width.
+ *
+ * @param arrowWidth The width of the arrow
+ * @return this
+ */
public BubbleLayout setArrowWidth(float arrowWidth) {
resetPadding();
this.arrowWidth = arrowWidth;
@@ -100,10 +140,21 @@ public class BubbleLayout extends LinearLayout {
return this;
}
+ /**
+ * Get the arrow height
+ *
+ * @return the height of the arrow
+ */
public float getArrowHeight() {
return arrowHeight;
}
+ /**
+ * Set the arrow height.
+ *
+ * @param arrowHeight The height of the arrow
+ * @return this
+ */
public BubbleLayout setArrowHeight(float arrowHeight) {
resetPadding();
this.arrowHeight = arrowHeight;
@@ -111,10 +162,21 @@ public class BubbleLayout extends LinearLayout {
return this;
}
+ /**
+ * Get the arrow position.
+ *
+ * @return the arrow position
+ */
public float getArrowPosition() {
return arrowPosition;
}
+ /**
+ * Get the arrow position.
+ *
+ * @param arrowPosition The arrow position
+ * @return this
+ */
public BubbleLayout setArrowPosition(float arrowPosition) {
resetPadding();
this.arrowPosition = arrowPosition;
@@ -122,30 +184,63 @@ public class BubbleLayout extends LinearLayout {
return this;
}
+ /**
+ * Get the corner radius
+ *
+ * @return the corner radius
+ */
public float getCornersRadius() {
return cornersRadius;
}
+ /**
+ * Set the corner radius
+ *
+ * @param cornersRadius The corner radius
+ * @return this
+ */
public BubbleLayout setCornersRadius(float cornersRadius) {
this.cornersRadius = cornersRadius;
requestLayout();
return this;
}
+ /**
+ * Get the bubble color.
+ *
+ * @return the bubble color
+ */
public int getBubbleColor() {
return bubbleColor;
}
+ /**
+ * Set the bubble color.
+ *
+ * @param bubbleColor The buble color
+ * @return this
+ */
public BubbleLayout setBubbleColor(int bubbleColor) {
this.bubbleColor = bubbleColor;
requestLayout();
return this;
}
+ /**
+ * Get stroke width.
+ *
+ * @return the stroke width
+ */
public float getStrokeWidth() {
return strokeWidth;
}
+ /**
+ * Set the stroke width.
+ *
+ * @param strokeWidth The stroke width
+ * @return this
+ */
public BubbleLayout setStrokeWidth(float strokeWidth) {
resetPadding();
this.strokeWidth = strokeWidth;
@@ -153,10 +248,21 @@ public class BubbleLayout extends LinearLayout {
return this;
}
+ /**
+ * Get the stroke color.
+ *
+ * @return the stroke color
+ */
public int getStrokeColor() {
return strokeColor;
}
+ /**
+ * Set the stroke color.
+ *
+ * @param strokeColor The stroke color
+ * @return this
+ */
public BubbleLayout setStrokeColor(int strokeColor) {
this.strokeColor = strokeColor;
requestLayout();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java
index 2ee17c227d..8749656e35 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java
@@ -36,7 +36,7 @@ public class Icon {
* @return The bitmap being used for the icon.
*/
public Bitmap getBitmap() {
- if (mBitmap.getConfig() != Bitmap.Config.ARGB_8888) {
+ if (mBitmap != null && mBitmap.getConfig() != Bitmap.Config.ARGB_8888) {
mBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888, false);
}
return mBitmap;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
index 57aa512401..3c9cb31211 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.annotations;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -23,7 +24,7 @@ import java.io.InputStream;
/**
* Factory for creating Icons from bitmap images.
* <p>
- * {@link Icon} is used to display bitmaps on top of the map using {@link Marker} and {@link MarkerView}.
+ * icon is used to display bitmaps on top of the map using {@link Marker} and {@link MarkerView}.
* </p>
*
* @see Icon
@@ -35,6 +36,7 @@ public final class IconFactory {
public static final String ICON_MARKERVIEW_ID = ICON_ID_PREFIX + "marker_view";
private Context context;
+ @SuppressLint("StaticFieldLeak")
private static IconFactory instance;
private Icon defaultMarker;
private Icon defaultMarkerView;
@@ -42,6 +44,12 @@ public final class IconFactory {
private int nextId = 0;
+ /**
+ * Get a single instance of IconFactory.
+ *
+ * @param context the context to derive the application context from
+ * @return the single instance of IconFactory
+ */
public static synchronized IconFactory getInstance(@NonNull Context context) {
if (instance == null) {
instance = new IconFactory(context.getApplicationContext());
@@ -71,10 +79,10 @@ public final class IconFactory {
}
/**
- * Creates an {@link Icon} from a given Bitmap image.
+ * Creates an icon from a given Bitmap image.
*
* @param bitmap image used for creating the Icon.
- * @return The {@link Icon} using the given Bitmap image.
+ * @return The icon using the given Bitmap image.
*/
public Icon fromBitmap(@NonNull Bitmap bitmap) {
if (nextId < 0) {
@@ -85,10 +93,10 @@ public final class IconFactory {
}
/**
- * Create an {@link Icon} using the resource ID of a Bitmap image.
+ * Creates an icon using the resource ID of a Bitmap image.
*
* @param resourceId The resource ID of a Bitmap image.
- * @return The {@link Icon} that was loaded from the asset or {@code null} if failed to load.
+ * @return The icon that was loaded from the asset or {@code null} if failed to load.
*/
public Icon fromResource(@DrawableRes int resourceId) {
Drawable drawable = ContextCompat.getDrawable(context, resourceId);
@@ -101,9 +109,9 @@ public final class IconFactory {
}
/**
- * Provides an {@link Icon} using the default marker icon used for {@link Marker}.
+ * Provides an icon using the default marker icon used for {@link Marker}.
*
- * @return An {@link Icon} with the default {@link Marker} icon.
+ * @return An icon with the default {@link Marker} icon.
*/
public Icon defaultMarker() {
if (defaultMarker == null) {
@@ -113,9 +121,9 @@ public final class IconFactory {
}
/**
- * Provides an {@link Icon} using the default marker icon used for {@link MarkerView}.
+ * Provides an icon using the default marker icon used for {@link MarkerView}.
*
- * @return An {@link Icon} with the default {@link MarkerView} icon.
+ * @return An icon with the default {@link MarkerView} icon.
*/
public Icon defaultMarkerView() {
if (defaultMarkerView == null) {
@@ -130,10 +138,10 @@ public final class IconFactory {
}
/**
- * Creates an {@link Icon} using the name of a Bitmap image in the assets directory.
+ * Creates an Icon using the name of a Bitmap image in the assets directory.
*
* @param assetName The name of a Bitmap image in the assets directory.
- * @return The {@link Icon} that was loaded from the asset or {@code null} if failed to load.
+ * @return The Icon that was loaded from the asset or null if failed to load.
*/
public Icon fromAsset(@NonNull String assetName) {
InputStream is;
@@ -146,11 +154,10 @@ public final class IconFactory {
}
/**
- * Creates an {@link Icon} using the absolute file path of a Bitmap image.
+ * Creates an Icon using the absolute file path of a Bitmap image.
*
* @param absolutePath The absolute path of the Bitmap image.
- * @return The {@link Icon} that was loaded from the absolute path or {@code null} if failed to
- * load.
+ * @return The Icon that was loaded from the absolute path or null if failed to load.
*/
public Icon fromPath(@NonNull String absolutePath) {
Bitmap bitmap = BitmapFactory.decodeFile(absolutePath, options);
@@ -158,11 +165,11 @@ public final class IconFactory {
}
/**
- * Create an {@link Icon} using the name of a Bitmap image file located in the internal storage.
- * In particular, this calls {@link Context#openFileInput(String)}.
+ * Create an Icon using the name of a Bitmap image file located in the internal storage.
+ * In particular, this calls Context#openFileInput(String).
*
* @param fileName The name of the Bitmap image file.
- * @return The {@link Icon} that was loaded from the asset or {@code null} if failed to load.
+ * @return The Icon that was loaded from the asset or null if failed to load.
* @see <a href="https://developer.android.com/guide/topics/data/data-storage.html#filesInternal">
* Using the Internal Storage</a>
*/
@@ -177,12 +184,11 @@ public final class IconFactory {
}
/**
- * Create an {@link Icon} using a previously created icon identifier along with a provided
- * Bitmap.
+ * Create an Icon using a previously created icon identifier along with a provided Bitmap.
*
- * @param iconId The {@link Icon} identifier you'd like to recreate.
+ * @param iconId The Icon identifier you'd like to recreate.
* @param bitmap a Bitmap used to replace the current one.
- * @return The {@link Icon} using the new Bitmap.
+ * @return The Icon using the new Bitmap.
*/
public static Icon recreate(@NonNull String iconId, @NonNull Bitmap bitmap) {
return new Icon(iconId, bitmap);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
index 56e8cc4ce2..eb82c7bf53 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
@@ -24,7 +24,10 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
* used with event listeners to bring up info windows. An {@link InfoWindow} is displayed by default
* when either a title or snippet is provided.
* </p>
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+@Deprecated
public class MarkerView extends Marker {
private MarkerViewManager markerViewManager;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
index bb51f3bfc2..8304d0e6ed 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
@@ -10,6 +10,7 @@ import android.support.v4.util.LongSparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.widget.ImageView;
import com.mapbox.mapboxsdk.R;
@@ -29,10 +30,22 @@ import java.util.Map;
* <p>
* This class is responsible for managing a {@link MarkerView} item.
* </p>
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+@Deprecated
public class MarkerViewManager implements MapView.OnMapChangedListener {
private final ViewGroup markerViewContainer;
+ private final ViewTreeObserver.OnPreDrawListener markerViewPreDrawObserver =
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ invalidateViewMarkersInVisibleRegion();
+ markerViewContainer.getViewTreeObserver().removeOnPreDrawListener(markerViewPreDrawObserver);
+ return false;
+ }
+ };
private final Map<MarkerView, View> markerViewMap = new HashMap<>();
private final LongSparseArray<OnMarkerViewAddedListener> markerViewAddedListenerMap = new LongSparseArray<>();
private final List<MapboxMap.MarkerViewAdapter> markerViewAdapters = new ArrayList<>();
@@ -125,6 +138,12 @@ public class MarkerViewManager implements MapView.OnMapChangedListener {
}
}
+ /**
+ * Set the rotation of a MarkerView to a given rotation value.
+ *
+ * @param marker The MarkerView to change its rotation value
+ * @param rotation The rotation value
+ */
public void setRotation(@NonNull MarkerView marker, float rotation) {
View convertView = markerViewMap.get(marker);
if (convertView != null) {
@@ -180,14 +199,16 @@ public class MarkerViewManager implements MapView.OnMapChangedListener {
PointF point = mapboxMap.getProjection().toScreenLocation(marker.getPosition());
if (marker.getOffsetX() == MapboxConstants.UNMEASURED) {
// ensure view is measured first
- if (marker.getWidth() == 0) {
- convertView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- if (convertView.getMeasuredWidth() != 0) {
- marker.setWidth(convertView.getMeasuredWidth());
- marker.setHeight(convertView.getMeasuredHeight());
- }
+ // #6805 invalidate marker views to ensure convertView width and height
+ // values are properly measured and up to date
+ if (marker.getWidth() == 0 && marker.isVisible()) {
+ convertView.getViewTreeObserver().addOnPreDrawListener(markerViewPreDrawObserver);
}
}
+
+ marker.setWidth(convertView.getWidth());
+ marker.setHeight(convertView.getHeight());
+
if (marker.getWidth() != 0) {
int x = (int) (marker.getAnchorU() * marker.getWidth());
int y = (int) (marker.getAnchorV() * marker.getHeight());
@@ -532,10 +553,11 @@ public class MarkerViewManager implements MapView.OnMapChangedListener {
}
/**
- * When the provided {@link MarkerView} is clicked on by a user, we check if a custom click
- * event has been created and if not, display a {@link InfoWindow}.
+ * When the provided MarkerView is clicked on by a user, we check if a custom click
+ * event has been created and if not, display a InfoWindow.
*
- * @param markerView that the click event occurred.
+ * @param markerView that the click event occurred
+ * @return true if the marker view click has been handled, false if not
*/
public boolean onClickMarkerView(MarkerView markerView) {
boolean clickHandled = false;
@@ -551,18 +573,13 @@ public class MarkerViewManager implements MapView.OnMapChangedListener {
clickHandled = onMarkerViewClickListener.onMarkerClick(markerView, view, adapter);
}
- if (!clickHandled) {
- ensureInfoWindowOffset(markerView);
- select(markerView, view, adapter);
- }
-
return clickHandled;
}
/**
- * Handles the {@link MarkerView}'s info window offset.
+ * Handles the MarkerView info window offset.
*
- * @param marker that we are ensuring info window offset.
+ * @param marker that we are ensuring info window offset
*/
public void ensureInfoWindowOffset(MarkerView marker) {
View view = null;
@@ -612,7 +629,7 @@ public class MarkerViewManager implements MapView.OnMapChangedListener {
}
/**
- * Default MarkerViewAdapter used for base class of {@link MarkerView} to adapt a MarkerView to
+ * Default MarkerViewAdapter used for base class of MarkerView to adapt a MarkerView to
* an ImageView.
*/
private static class ImageMarkerViewAdapter extends MapboxMap.MarkerViewAdapter<MarkerView> {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java
index 2d829537fc..79c72e5f70 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java
@@ -12,7 +12,10 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
* <p>
* Do not extend this class directly but extend {@link BaseMarkerViewOptions} instead.
* </p>
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+@Deprecated
public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerViewOptions> {
private MarkerView marker;
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 66c261f1d0..c2f19072db 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
@@ -3,6 +3,7 @@ package com.mapbox.mapboxsdk.camera;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.FloatRange;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
@@ -166,14 +167,14 @@ public final class CameraPosition implements Parcelable {
private double zoom = -1;
/**
- * Creates an empty builder.
+ * Create an empty builder.
*/
public Builder() {
super();
}
/**
- * Create Builder with an existing CameraPosition data.
+ * Create a builder with an existing CameraPosition data.
*
* @param previous Existing CameraPosition values to use
*/
@@ -188,7 +189,7 @@ public final class CameraPosition implements Parcelable {
}
/**
- * Create Builder with an existing CameraPosition data.
+ * Create a builder with an existing CameraPosition data.
*
* @param typedArray TypedArray containing attribute values
*/
@@ -205,7 +206,7 @@ public final class CameraPosition implements Parcelable {
}
/**
- * Create Builder from an existing CameraPositionUpdate update.
+ * Create a builder from an existing CameraPositionUpdate update.
*
* @param update Update containing camera options
*/
@@ -220,7 +221,7 @@ public final class CameraPosition implements Parcelable {
}
/**
- * Create Builder from an existing CameraPositionUpdate update.
+ * Create builder from an existing CameraPositionUpdate update.
*
* @param update Update containing camera options
*/
@@ -235,7 +236,7 @@ public final class CameraPosition implements Parcelable {
* Sets the direction that the camera is pointing in, in degrees clockwise from north.
*
* @param bearing Bearing
- * @return Builder
+ * @return this
*/
public Builder bearing(double bearing) {
double direction = bearing;
@@ -252,19 +253,10 @@ public final class CameraPosition implements Parcelable {
}
/**
- * Builds a CameraPosition.
+ * Sets the location where the camera is pointing at.
*
- * @return CameraPosition
- */
- public CameraPosition build() {
- return new CameraPosition(target, zoom, tilt, bearing);
- }
-
- /**
- * Sets the location that the camera is pointing at.
- *
- * @param location Location
- * @return Builder
+ * @param location target of the camera
+ * @return this
*/
public Builder target(LatLng location) {
this.target = location;
@@ -272,28 +264,42 @@ public final class CameraPosition implements Parcelable {
}
/**
- * Set the tilt in degrees
+ * Set the tilt of the camera in degrees
* <p>
- * value is clamped to 0 and 60.
+ * value is clamped to {@link MapboxConstants#MINIMUM_TILT} and {@link MapboxConstants#MAXIMUM_TILT}.
* </p>
*
- * @param tilt Tilt value
- * @return Builder
+ * @param tilt Tilt value of the camera
+ * @return this
*/
- public Builder tilt(double tilt) {
+ public Builder tilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT,
+ to = MapboxConstants.MAXIMUM_TILT) double tilt) {
this.tilt = MathUtils.clamp(tilt, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT);
return this;
}
/**
- * Set the zoom
+ * Set the zoom of the camera
+ * <p>
+ * Zoom ranges from {@link MapboxConstants#MINIMUM_ZOOM} to {@link MapboxConstants#MAXIMUM_ZOOM}
+ * </p>
*
- * @param zoom Zoom value
- * @return Builder
+ * @param zoom Zoom value of the camera
+ * @return this
*/
- public Builder zoom(double zoom) {
+ public Builder zoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM,
+ to = MapboxConstants.MAXIMUM_ZOOM) double zoom) {
this.zoom = zoom;
return this;
}
+
+ /**
+ * Builds the CameraPosition.
+ *
+ * @return CameraPosition
+ */
+ public CameraPosition build() {
+ return new CameraPosition(target, zoom, tilt, bearing);
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java
index 7e0dbf08fb..31f13cbcff 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java
@@ -1,14 +1,22 @@
package com.mapbox.mapboxsdk.camera;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import com.mapbox.mapboxsdk.maps.MapboxMap;
/**
- * Interface definition for camera position changes.
+ * Interface definition for camera updates.
*/
public interface CameraUpdate {
+ /**
+ * Get the camera position from the camera update.
+ *
+ * @param mapboxMap Map object to build the position from
+ * @return the camera position from the implementing camera update
+ */
+ @Nullable
CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap);
}
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 8e1411e273..50e33f4f9f 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
@@ -42,7 +42,7 @@ public final class CameraUpdateFactory {
}
/**
- * Returns a {@link CameraUpdate} that transforms the camera such that the specified
+ * Returns a CameraUpdate that transforms the camera such that the specified
* latitude/longitude bounds are centered on screen at the greatest possible zoom level.
* You can specify padding, in order to inset the bounding box from the map view's edges.
* The returned CameraUpdate has a bearing of 0 and a tilt of 0.
@@ -56,7 +56,7 @@ public final class CameraUpdateFactory {
}
/**
- * Returns a {@link CameraUpdate} that transforms the camera such that the specified
+ * Returns a CameraUpdate that transforms the camera such that the specified
* latitude/longitude bounds are centered on screen at the greatest possible zoom level.
* You can specify padding, in order to inset the bounding box from the map view's edges.
* The returned CameraUpdate has a bearing of 0 and a tilt of 0.
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..1a7544d33a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java
@@ -0,0 +1,63 @@
+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 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 ecb6ffe24e..3b35df4f4b 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
@@ -55,7 +55,7 @@ public class MapboxConstants {
/**
* The currently supported maximum zoom level.
*/
- public static final float MAXIMUM_ZOOM = 20.0f;
+ public static final float MAXIMUM_ZOOM = 25.5f;
/**
* The currently supported maximum tilt value.
@@ -80,12 +80,12 @@ public class MapboxConstants {
/**
* The currently used minimun scale factor to clamp to when a quick zoom gesture occurs
*/
- public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.65f;
+ 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 = 1.35f;
+ public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.45f;
/**
* Fragment Argument Key for MapboxMapOptions
@@ -133,5 +133,17 @@ 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";
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
index 88c3bef673..ceac862f39 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
@@ -2,23 +2,27 @@ 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 bearing tracking modes.
+ * 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 {
- /**
- * Indicates that the parameter accepts one of the values from MyBearingTracking.
- */
- @IntDef( {NONE, COMPASS, GPS, /**COMBINED**/})
+ @IntDef( {NONE, COMPASS, GPS})
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {
}
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
index a1744d701f..1283283fa5 100644
--- 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
@@ -3,29 +3,34 @@ 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 different types of locational tracking modes.
+ * 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 com.mapbox.mapboxsdk.maps.TrackingSettings#setMyLocationTrackingMode(int)
- * @see MyLocationView#setMyLocationTrackingMode(int)
+ * @see MapboxMap#setMyLocationEnabled(boolean)
+ * @see TrackingSettings#setMyLocationTrackingMode(int)
*/
public class MyLocationTracking {
- /**
- * Indicates the parameter accepts one of the values from MyLocationTracking.
- */
@IntDef( {TRACKING_NONE, TRACKING_FOLLOW})
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {
}
/**
- * Location tracking is disabled.
+ * Tracking the location of the user is disabled.
*/
public static final int TRACKING_NONE = 0x00000000;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
index 31e6313509..9943a72e06 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/Style.java
@@ -17,7 +17,6 @@ import java.lang.annotation.RetentionPolicy;
*/
public class Style {
-
/**
* Indicates the parameter accepts one of the values from Style. Using one of these
* constants means your map style will always use the latest version and may change as we
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java
index 7154049bd7..1f6b0efc4d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java
@@ -1,15 +1,13 @@
package com.mapbox.mapboxsdk.exceptions;
-import android.graphics.Bitmap;
-
import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.maps.MapView;
/**
* <p>
- * A {@code IconBitmapChangedException} is thrown by {@link MapView} when a {@link Marker} is added
- * that has a {@link Icon} with a {@link Bitmap} that has been modified since the creation of the Icon.
+ * An IconBitmapChangedException is thrown by MapView when a Marker is added
+ * that has an Icon with a Bitmap that has been modified since the creation of the Icon.
* </p>
* You cannot modify a {@code Icon} after it has been added to the map in a {@code Marker}
*
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java
index 08a23a7373..c1d0385815 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java
@@ -1,8 +1,8 @@
package com.mapbox.mapboxsdk.exceptions;
/**
- * A InvalidLatLngBoundsException is thrown by {@link com.mapbox.mapboxsdk.geometry.LatLngBounds}
- * when there aren't enough {@link com.mapbox.mapboxsdk.geometry.LatLng} to create a bounds.
+ * An InvalidLatLngBoundsException is thrown by LatLngBounds
+ * when there aren't enough LatLng to create a bounds.
*/
public class InvalidLatLngBoundsException extends RuntimeException {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java
index f0f9b9236b..44ee83265d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java
@@ -5,6 +5,9 @@ package com.mapbox.mapboxsdk.exceptions;
*/
public class InvalidMarkerPositionException extends RuntimeException {
+ /**
+ * Creates a invalid marker position exception thrown when a Marker object is created with an invalid LatLng position.
+ */
public InvalidMarkerPositionException() {
super("Adding an invalid Marker to a Map. "
+ "Missing the required position field. "
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java
index 74bceb196c..e9a0261d85 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java
@@ -8,10 +8,14 @@ import android.content.Context;
* This occurs either when {@link com.mapbox.mapboxsdk.Mapbox} is not correctly initialised or the provided access token
* through {@link com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)} isn't valid.
* </p>
+ *
* @see com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)
*/
public class MapboxConfigurationException extends RuntimeException {
+ /**
+ * Creates a Mapbox configuration exception thrown by MapboxMap when the SDK hasn't been properly initialised.
+ */
public MapboxConfigurationException() {
super("\nUsing MapView requires setting a valid access token. Use Mapbox.getInstance(Context context, "
+ "String accessToken) to provide one. "
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java
index 8923d822f2..bffc10dc04 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java
@@ -1,12 +1,11 @@
package com.mapbox.mapboxsdk.exceptions;
-import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.IconFactory;
/**
* <p>
- * A {@code TooManyIconsException} is thrown by {@link IconFactory} when it
- * cannot create a {@link Icon} because there are already too many icons created.
+ * A TooManyIconsException is thrown by IconFactory when it
+ * cannot create a Icon because there are already too many icons created.
* </p>
* You should try to reuse Icon objects whenever possible.
*
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ILatLng.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ILatLng.java
index 1af8e7cfc7..07df87af3a 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ILatLng.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ILatLng.java
@@ -1,12 +1,28 @@
package com.mapbox.mapboxsdk.geometry;
/**
- * Describes a latitude, longitude point.
+ * Describes a latitude, longitude, and altitude tuple.
*/
public interface ILatLng {
+
+ /**
+ * Get the latitude, in degrees.
+ *
+ * @return the latitude value in degrees
+ */
double getLatitude();
+ /**
+ * Get the longitude, in degrees.
+ *
+ * @return the longitude value in degrees
+ */
double getLongitude();
+ /**
+ * Get the altitude, in meters.
+ *
+ * @return the altitude value in meters
+ */
double getAltitude();
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/IProjectedMeters.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/IProjectedMeters.java
index 694c935143..db459d7cbb 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/IProjectedMeters.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/IProjectedMeters.java
@@ -4,7 +4,19 @@ package com.mapbox.mapboxsdk.geometry;
* Describes a projection in Mercator meters.
*/
public interface IProjectedMeters {
+
+ /**
+ * Get the north projection, in meters.
+ *
+ * @return the projected meters in north direction
+ */
double getNorthing();
+ /**
+ * Get the east projection, in meters.
+ *
+ * @return the projected meters in east direction
+ */
double getEasting();
+
}
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 ca2d3673b2..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
@@ -3,9 +3,10 @@ package com.mapbox.mapboxsdk.geometry;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.FloatRange;
+
+import com.mapbox.mapboxsdk.constants.GeometryConstants;
-import com.mapbox.services.android.telemetry.constants.GeoConstants;
-import com.mapbox.services.android.telemetry.utils.MathUtils;
/**
* A geographical location which contains a single latitude, longitude pair, with
@@ -21,6 +22,9 @@ import com.mapbox.services.android.telemetry.utils.MathUtils;
*/
public class LatLng implements ILatLng, Parcelable {
+ /**
+ * Inner class responsible for recreating Parcels into objects.
+ */
public static final Parcelable.Creator<LatLng> CREATOR = new Parcelable.Creator<LatLng>() {
public LatLng createFromParcel(Parcel in) {
return new LatLng(in);
@@ -44,7 +48,7 @@ public class LatLng implements ILatLng, Parcelable {
}
/**
- * Construct a new latitude, longitude point given float arguments
+ * Construct a new latitude, longitude point given double arguments
*
* @param latitude Latitude in degrees
* @param longitude Longitude in degrees
@@ -55,7 +59,7 @@ public class LatLng implements ILatLng, Parcelable {
}
/**
- * Construct a new latitude, longitude, altitude point given float arguments
+ * Construct a new latitude, longitude, altitude point given double arguments
*
* @param latitude Latitude in degrees
* @param longitude Longitude in degress
@@ -68,7 +72,7 @@ public class LatLng implements ILatLng, Parcelable {
}
/**
- * Transform a Location into a LatLng point
+ * Construct a new latitude, longitude, altitude point given location argument
*
* @param location Android Location
*/
@@ -77,38 +81,78 @@ public class LatLng implements ILatLng, Parcelable {
}
/**
- * Clone an existing latitude longitude point
+ * Construct a new latitude, longitude, altitude point given another latitude, longitude, altitude point.
*
- * @param aLatLng LatLng
+ * @param latLng LatLng to be cloned.
*/
- public LatLng(LatLng aLatLng) {
- this.latitude = aLatLng.latitude;
- this.longitude = aLatLng.longitude;
- this.altitude = aLatLng.altitude;
+ public LatLng(LatLng latLng) {
+ this.latitude = latLng.latitude;
+ this.longitude = latLng.longitude;
+ this.altitude = latLng.altitude;
}
+ /**
+ * Constructs a new latitude, longitude, altitude tuple given a parcel.
+ *
+ * @param in the parcel containing the latitude, longitude, altitude values
+ */
protected LatLng(Parcel in) {
setLatitude(in.readDouble());
setLongitude(in.readDouble());
setAltitude(in.readDouble());
}
- public void setLatitude(double latitude) {
+ /**
+ * Set the latitude, in degrees.
+ * <p>
+ * 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 GeometryConstants#MIN_LATITUDE
+ * @see GeometryConstants#MAX_LATITUDE
+ */
+ public void setLatitude(
+ @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;
}
+ /**
+ * Get the latitude, in degrees.
+ * <p>
+ * 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 GeometryConstants#MIN_LATITUDE
+ * @see GeometryConstants#MAX_LATITUDE
+ */
@Override
public double getLatitude() {
return latitude;
}
- public void setLongitude(double longitude) {
+ /**
+ * Set the longitude, in degrees.
+ * <p>
+ * 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 GeometryConstants#MIN_LONGITUDE
+ * @see GeometryConstants#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");
}
@@ -118,15 +162,36 @@ public class LatLng implements ILatLng, Parcelable {
this.longitude = longitude;
}
+ /**
+ * Get the longitude, in degrees.
+ * <p>
+ * 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 GeometryConstants#MIN_LONGITUDE
+ * @see GeometryConstants#MAX_LONGITUDE
+ */
@Override
public double getLongitude() {
return longitude;
}
+ /**
+ * Set the altitude, in meters.
+ *
+ * @param altitude the altitude in meters
+ */
public void setAltitude(double altitude) {
this.altitude = altitude;
}
+ /**
+ * Get the altitude, in meters.
+ *
+ * @return the altitude value in meters
+ */
@Override
public double getAltitude() {
return altitude;
@@ -136,13 +201,44 @@ public class LatLng implements ILatLng, Parcelable {
* Return a new LatLng object with a wrapped Longitude. This allows original data object
* to remain unchanged.
*
- * @return New LatLng object with wrapped Longitude
+ * @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;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param object The object to compare
+ * @return True if equal, false if not
+ */
@Override
public boolean equals(Object object) {
if (this == object) {
@@ -158,6 +254,11 @@ public class LatLng implements ILatLng, Parcelable {
&& Double.compare(latLng.longitude, longitude) == 0;
}
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return the hash code value
+ */
@Override
public int hashCode() {
int result;
@@ -171,16 +272,32 @@ public class LatLng implements ILatLng, Parcelable {
return result;
}
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return the string representation
+ */
@Override
public String toString() {
return "LatLng [latitude=" + latitude + ", longitude=" + longitude + ", altitude=" + altitude + "]";
}
+ /**
+ * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param out The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written
+ */
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeDouble(latitude);
@@ -213,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 4a4e2a30aa..6d027a0cef 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
@@ -3,9 +3,10 @@ package com.mapbox.mapboxsdk.geometry;
import android.os.Parcel;
import android.os.Parcelable;
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;
@@ -18,10 +19,10 @@ import java.util.List;
*/
public class LatLngBounds implements Parcelable {
- private final double mLatNorth;
- private final double mLatSouth;
- private final double mLonEast;
- private final double mLonWest;
+ private final double latitudeNorth;
+ private final double latitudeSouth;
+ private final double longitudeEast;
+ private final double longitudeWest;
/**
* Construct a new LatLngBounds based on its corners, given in NESW
@@ -34,10 +35,10 @@ public class LatLngBounds implements Parcelable {
*/
LatLngBounds(final double northLatitude, final double eastLongitude, final double southLatitude,
final double westLongitude) {
- this.mLatNorth = northLatitude;
- this.mLonEast = eastLongitude;
- this.mLatSouth = southLatitude;
- this.mLonWest = westLongitude;
+ this.latitudeNorth = northLatitude;
+ this.longitudeEast = eastLongitude;
+ this.latitudeSouth = southLatitude;
+ this.longitudeWest = westLongitude;
}
/**
@@ -47,8 +48,8 @@ public class LatLngBounds implements Parcelable {
*/
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))
+ .include(new LatLng(GeometryConstants.MAX_LATITUDE, GeometryConstants.MAX_LONGITUDE))
+ .include(new LatLng(GeometryConstants.MIN_LATITUDE, GeometryConstants.MIN_LONGITUDE))
.build();
}
@@ -59,8 +60,8 @@ public class LatLngBounds implements Parcelable {
* @return LatLng center of this LatLngBounds
*/
public LatLng getCenter() {
- return new LatLng((this.mLatNorth + this.mLatSouth) / 2,
- (this.mLonEast + this.mLonWest) / 2);
+ return new LatLng((this.latitudeNorth + this.latitudeSouth) / 2,
+ (this.longitudeEast + this.longitudeWest) / 2);
}
/**
@@ -69,7 +70,7 @@ public class LatLngBounds implements Parcelable {
* @return double latitude value for north
*/
public double getLatNorth() {
- return this.mLatNorth;
+ return this.latitudeNorth;
}
/**
@@ -78,7 +79,7 @@ public class LatLngBounds implements Parcelable {
* @return double latitude value for south
*/
public double getLatSouth() {
- return this.mLatSouth;
+ return this.latitudeSouth;
}
/**
@@ -87,7 +88,7 @@ public class LatLngBounds implements Parcelable {
* @return double longitude value for east
*/
public double getLonEast() {
- return this.mLonEast;
+ return this.longitudeEast;
}
/**
@@ -96,7 +97,7 @@ public class LatLngBounds implements Parcelable {
* @return double longitude value for west
*/
public double getLonWest() {
- return this.mLonWest;
+ return this.longitudeWest;
}
/**
@@ -105,7 +106,7 @@ public class LatLngBounds implements Parcelable {
* @return LatLng of the south west corner
*/
public LatLng getSouthWest() {
- return new LatLng(mLatSouth, mLonWest);
+ return new LatLng(latitudeSouth, longitudeWest);
}
/**
@@ -114,7 +115,7 @@ public class LatLngBounds implements Parcelable {
* @return LatLng of the north east corner
*/
public LatLng getNorthEast() {
- return new LatLng(mLatNorth, mLonEast);
+ return new LatLng(latitudeNorth, longitudeEast);
}
/**
@@ -123,7 +124,7 @@ public class LatLngBounds implements Parcelable {
* @return LatLng of the south east corner
*/
public LatLng getSouthEast() {
- return new LatLng(mLatSouth, mLonEast);
+ return new LatLng(latitudeSouth, longitudeEast);
}
/**
@@ -132,7 +133,7 @@ public class LatLngBounds implements Parcelable {
* @return LatLng of the north west corner
*/
public LatLng getNorthWest() {
- return new LatLng(mLatNorth, mLonWest);
+ return new LatLng(latitudeNorth, longitudeWest);
}
/**
@@ -151,7 +152,7 @@ public class LatLngBounds implements Parcelable {
* @return Span distance
*/
public double getLatitudeSpan() {
- return Math.abs(this.mLatNorth - this.mLatSouth);
+ return Math.abs(this.latitudeNorth - this.latitudeSouth);
}
/**
@@ -161,7 +162,7 @@ public class LatLngBounds implements Parcelable {
* @return Span distance
*/
public double getLongitudeSpan() {
- return Math.abs(this.mLonEast - this.mLonWest);
+ return Math.abs(this.longitudeEast - this.longitudeWest);
}
@@ -174,9 +175,15 @@ public class LatLngBounds implements Parcelable {
return getLongitudeSpan() == 0.0 || getLatitudeSpan() == 0.0;
}
+ /**
+ * Returns a string representaton of the object.
+ *
+ * @return the string representation
+ */
@Override
public String toString() {
- return "N:" + this.mLatNorth + "; E:" + this.mLonEast + "; S:" + this.mLatSouth + "; W:" + this.mLonWest;
+ return "N:" + this.latitudeNorth + "; E:" + this.longitudeEast + "; S:" + this.latitudeSouth
+ + "; W:" + this.longitudeWest;
}
/**
@@ -187,10 +194,10 @@ 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 minLon = GeometryConstants.MAX_LONGITUDE;
+ double maxLat = GeometryConstants.MIN_LATITUDE;
+ double maxLon = GeometryConstants.MIN_LONGITUDE;
for (final ILatLng gp : latLngs) {
final double latitude = gp.getLatitude();
@@ -215,6 +222,40 @@ public class LatLngBounds implements Parcelable {
}
/**
+ * Constructs a LatLngBounds from doubles representing a LatLng pair.
+ * <p>
+ * This method doesn't recalculate most east or most west boundaries.
+ * </p>
+ */
+ public static LatLngBounds from(double latNorth, double lonEast, double latSouth, double lonWest) {
+ return new LatLngBounds(latNorth, lonEast, latSouth, lonWest);
+ }
+
+ private static double lat_(int z, int y) {
+ double n = Math.PI - 2.0 * Math.PI * y / Math.pow(2.0, z);
+ return Math.toDegrees(Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
+ }
+
+ private static double lon_(int z, int x) {
+ return x / Math.pow(2.0, z) * 360.0 - GeometryConstants.MAX_LONGITUDE;
+ }
+
+ /**
+ * Constructs a LatLngBounds from a Tile identifier.
+ *
+ * Returned bounds will have latitude in the range of Mercator projection.
+ * @see GeometryConstants#MIN_MERCATOR_LATITUDE
+ * @see GeometryConstants#MAX_MERCATOR_LATITUDE
+ *
+ * @param z Tile zoom level.
+ * @param x Tile X coordinate.
+ * @param y Tile Y coordinate.
+ */
+ public static LatLngBounds from(int z, int x, int y) {
+ return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x));
+ }
+
+ /**
* Constructs a LatLngBounds from current bounds with an additional latitude-longitude pair.
*
* @param latLng the latitude lognitude pair to include in the bounds.
@@ -241,10 +282,10 @@ public class LatLngBounds implements Parcelable {
}
if (o instanceof LatLngBounds) {
LatLngBounds other = (LatLngBounds) o;
- return mLatNorth == other.getLatNorth()
- && mLatSouth == other.getLatSouth()
- && mLonEast == other.getLonEast()
- && mLonWest == other.getLonWest();
+ return latitudeNorth == other.getLatNorth()
+ && latitudeSouth == other.getLatSouth()
+ && longitudeEast == other.getLonEast()
+ && longitudeWest == other.getLonWest();
}
return false;
}
@@ -258,10 +299,10 @@ public class LatLngBounds implements Parcelable {
public boolean contains(final ILatLng latLng) {
final double latitude = latLng.getLatitude();
final double longitude = latLng.getLongitude();
- return ((latitude <= this.mLatNorth)
- && (latitude >= this.mLatSouth))
- && ((longitude <= this.mLonEast)
- && (longitude >= this.mLonWest));
+ return ((latitude <= this.latitudeNorth)
+ && (latitude >= this.latitudeSouth))
+ && ((longitude <= this.longitudeEast)
+ && (longitude >= this.longitudeWest));
}
/**
@@ -288,17 +329,17 @@ public class LatLngBounds implements Parcelable {
* Returns a new LatLngBounds that stretches to include another LatLngBounds,
* given by corner points.
*
- * @param lonNorth Northern Longitude
- * @param latEast Eastern Latitude
- * @param lonSouth Southern Longitude
- * @param latWest Western Longitude
+ * @param latNorth Northern Latitude
+ * @param lonEast Eastern Longitude
+ * @param latSouth Southern Latitude
+ * @param lonWest Western Longitude
* @return BoundingBox
*/
- public LatLngBounds union(final double lonNorth, final double latEast, final double lonSouth, final double latWest) {
- return new LatLngBounds((this.mLatNorth < lonNorth) ? lonNorth : this.mLatNorth,
- (this.mLonEast < latEast) ? latEast : this.mLonEast,
- (this.mLatSouth > lonSouth) ? lonSouth : this.mLatSouth,
- (this.mLonWest > latWest) ? latWest : this.mLonWest);
+ public LatLngBounds union(final double latNorth, final double lonEast, final double latSouth, final double lonWest) {
+ return new LatLngBounds((this.latitudeNorth < latNorth) ? latNorth : this.latitudeNorth,
+ (this.longitudeEast < lonEast) ? lonEast : this.longitudeEast,
+ (this.latitudeSouth > latSouth) ? latSouth : this.latitudeSouth,
+ (this.longitudeWest > lonWest) ? lonWest : this.longitudeWest);
}
/**
@@ -307,14 +348,15 @@ public class LatLngBounds implements Parcelable {
* @param box LatLngBounds to intersect with
* @return LatLngBounds
*/
+ @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;
@@ -334,6 +376,9 @@ public class LatLngBounds implements Parcelable {
return intersect(new LatLngBounds(northLatitude, eastLongitude, southLatitude, westLongitude));
}
+ /**
+ * Inner class responsible for recreating Parcels into objects.
+ */
public static final Parcelable.Creator<LatLngBounds> CREATOR =
new Parcelable.Creator<LatLngBounds>() {
@Override
@@ -347,25 +392,41 @@ public class LatLngBounds implements Parcelable {
}
};
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return the hash code
+ */
@Override
public int hashCode() {
- return (int) ((mLatNorth + 90)
- + ((mLatSouth + 90) * 1000)
- + ((mLonEast + 180) * 1000000)
- + ((mLonEast + 180) * 1000000000));
+ return (int) ((latitudeNorth + 90)
+ + ((latitudeSouth + 90) * 1000)
+ + ((longitudeEast + 180) * 1000000)
+ + ((longitudeEast + 180) * 1000000000));
}
+ /**
+ * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param out The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written
+ */
@Override
- public void writeToParcel(final Parcel out, final int arg1) {
- out.writeDouble(this.mLatNorth);
- out.writeDouble(this.mLonEast);
- out.writeDouble(this.mLatSouth);
- out.writeDouble(this.mLonWest);
+ public void writeToParcel(final Parcel out, final int flags) {
+ out.writeDouble(this.latitudeNorth);
+ out.writeDouble(this.longitudeEast);
+ out.writeDouble(this.latitudeSouth);
+ out.writeDouble(this.longitudeWest);
}
private static LatLngBounds readFromParcel(final Parcel in) {
@@ -381,28 +442,53 @@ public class LatLngBounds implements Parcelable {
*/
public static final class Builder {
- private List<LatLng> mLatLngList;
+ private List<LatLng> latLngList;
+ /**
+ * Constructs a builder to compose LatLng objects to a LatLngBounds.
+ */
public Builder() {
- mLatLngList = new ArrayList<>();
+ latLngList = new ArrayList<>();
}
+ /**
+ * Builds a new LatLngBounds.
+ * <p>
+ * Throws an {@link InvalidLatLngBoundsException} when no LatLngBounds can be created.
+ * </p>
+ *
+ * @return the build LatLngBounds
+ */
public LatLngBounds build() {
- if (mLatLngList.size() < 2) {
- throw new InvalidLatLngBoundsException(mLatLngList.size());
+ if (latLngList.size() < 2) {
+ throw new InvalidLatLngBoundsException(latLngList.size());
}
- return LatLngBounds.fromLatLngs(mLatLngList);
+ return LatLngBounds.fromLatLngs(latLngList);
}
+ /**
+ * Adds a LatLng object to the LatLngBounds.Builder.
+ *
+ * @param latLngs the List of LatLng objects to be added
+ * @return this
+ */
public Builder includes(List<LatLng> latLngs) {
for (LatLng point : latLngs) {
- mLatLngList.add(point);
+ include(point);
}
return this;
}
+ /**
+ * Adds a LatLng object to the LatLngBounds.Builder.
+ *
+ * @param latLng the LatLng to be added
+ * @return this
+ */
public Builder include(@NonNull LatLng latLng) {
- mLatLngList.add(latLng);
+ if (!latLngList.contains(latLng)) {
+ latLngList.add(latLng);
+ }
return this;
}
}
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 d00ccdb9b8..322c7dfb74 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
@@ -64,19 +64,28 @@ public class LatLngSpan implements Parcelable {
mLongitudeSpan = longitudeSpan;
}
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param object The object to compare
+ * @return True if equal, false if not
+ */
@Override
- public boolean equals(Object o) {
- if (this == o) {
+ public boolean equals(Object object) {
+ if (this == object) {
return true;
}
- if (o instanceof LatLngSpan) {
- LatLngSpan other = (LatLngSpan) o;
+ if (object instanceof LatLngSpan) {
+ LatLngSpan other = (LatLngSpan) object;
return mLongitudeSpan == other.getLongitudeSpan()
&& mLatitudeSpan == other.getLatitudeSpan();
}
return false;
}
+ /**
+ * Inner class responsible for recreating Parcels into objects.
+ */
public static final Parcelable.Creator<LatLngSpan> CREATOR =
new Parcelable.Creator<LatLngSpan>() {
@Override
@@ -90,14 +99,41 @@ public class LatLngSpan implements Parcelable {
}
};
+ /**
+ * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param out Parcel in which the object should be written
+ * @param flags Additional flags about how the object should be written
+ */
@Override
- public void writeToParcel(Parcel out, int arg1) {
+ public void writeToParcel(Parcel out, int flags) {
out.writeDouble(mLatitudeSpan);
out.writeDouble(mLongitudeSpan);
}
+
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return hash code value of this
+ */
+ @Override
+ public int hashCode() {
+ int result;
+ long temp;
+ temp = Double.doubleToLongBits(mLatitudeSpan);
+ result = (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(mLongitudeSpan);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java
index 761d8f2a8b..fa84c33b2b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java
@@ -13,6 +13,9 @@ import android.os.Parcelable;
*/
public class ProjectedMeters implements IProjectedMeters, Parcelable {
+ /**
+ * Inner class responsible for recreating Parcels into objects.
+ */
public static final Creator<ProjectedMeters> CREATOR = new Creator<ProjectedMeters>() {
public ProjectedMeters createFromParcel(Parcel in) {
return new ProjectedMeters(in);
@@ -47,6 +50,12 @@ public class ProjectedMeters implements IProjectedMeters, Parcelable {
this.easting = projectedMeters.easting;
}
+ /**
+ * Creates a ProjectedMeters from a Parcel.
+ *
+ * @param in The parcel to create from
+ * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
+ */
private ProjectedMeters(Parcel in) {
northing = in.readDouble();
easting = in.readDouble();
@@ -72,22 +81,32 @@ public class ProjectedMeters implements IProjectedMeters, Parcelable {
return easting;
}
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param other The object to compare this to
+ * @return true if equal, false if not
+ */
@Override
- public boolean equals(Object o) {
- if (this == o) {
+ public boolean equals(Object other) {
+ if (this == other) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (other == null || getClass() != other.getClass()) {
return false;
}
- ProjectedMeters projectedMeters = (ProjectedMeters) o;
+ ProjectedMeters projectedMeters = (ProjectedMeters) other;
return Double.compare(projectedMeters.easting, easting) == 0
&& Double.compare(projectedMeters.northing, northing) == 0;
-
}
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return the hash code of this
+ */
@Override
public int hashCode() {
int result;
@@ -99,16 +118,32 @@ public class ProjectedMeters implements IProjectedMeters, Parcelable {
return result;
}
+ /**
+ * Returns a string representation of the object.
+ *
+ * @return the string representation of this
+ */
@Override
public String toString() {
return "ProjectedMeters [northing=" + northing + ", easting=" + easting + "]";
}
+ /**
+ * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param out The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written
+ */
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeDouble(northing);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java
index c5b8ad3077..c09c00fced 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java
@@ -37,6 +37,11 @@ public class VisibleRegion implements Parcelable {
*/
public final LatLngBounds latLngBounds;
+ /**
+ * Creates a VisibleRegion from a Parcel.
+ *
+ * @param in The Parcel to create this from
+ */
private VisibleRegion(Parcel in) {
this.farLeft = in.readParcelable(LatLng.class.getClassLoader());
this.farRight = in.readParcelable(LatLng.class.getClassLoader());
@@ -46,7 +51,7 @@ public class VisibleRegion implements Parcelable {
}
/**
- * Creates a new VisibleRegion given the four corners of the camera.
+ * Creates a VisibleRegion given the four corners of the camera.
*
* @param farLeft A LatLng object containing the latitude and longitude of the near left corner of the region.
* @param farRight A LatLng object containing the latitude and longitude of the near left corner of the region.
@@ -88,12 +93,22 @@ public class VisibleRegion implements Parcelable {
&& latLngBounds.equals(visibleRegion.latLngBounds);
}
+ /**
+ * The string representation of the object.
+ *
+ * @return the string representation of this
+ */
@Override
public String toString() {
return "[farLeft [" + farLeft + "], farRight [" + farRight + "], nearLeft [" + nearLeft + "], nearRight ["
+ nearRight + "], latLngBounds [" + latLngBounds + "]]";
}
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return the hash code
+ */
@Override
public int hashCode() {
return ((farLeft.hashCode() + 90)
@@ -102,11 +117,22 @@ public class VisibleRegion implements Parcelable {
+ ((nearRight.hashCode() + 180) * 1000000000));
}
+ /**
+ * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation.
+ *
+ * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance.
+ */
@Override
public int describeContents() {
return 0;
}
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param out The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written
+ */
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeParcelable(farLeft, flags);
@@ -116,6 +142,9 @@ public class VisibleRegion implements Parcelable {
out.writeParcelable(latLngBounds, flags);
}
+ /**
+ * Inner class responsible for recreating Parcels into objects.
+ */
public static final Parcelable.Creator<VisibleRegion> CREATOR =
new Parcelable.Creator<VisibleRegion>() {
public VisibleRegion createFromParcel(Parcel in) {
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 9c8cda5544..32aa250997 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
@@ -56,13 +56,14 @@ class HTTPRequest implements Callback {
mNativePtr = nativePtr;
try {
- // Don't try a request if we aren't connected
- if (!Mapbox.isConnected()) {
+ HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
+ final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
+
+ // Don't try a request to remote server if we aren't connected
+ if (!Mapbox.isConnected() && !host.equals("127.0.0.1") && !host.equals("localhost")) {
throw new NoRouteToHostException("No Internet connection available.");
}
- HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
- final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
if (host.equals("mapbox.com") || host.endsWith(".mapbox.com") || host.equals("mapbox.cn")
|| host.endsWith(".mapbox.cn")) {
if (httpUrl.querySize() == 0) {
@@ -84,7 +85,14 @@ class HTTPRequest implements Callback {
}
mRequest = builder.build();
mCall = mClient.newCall(mRequest);
- mCall.enqueue(this);
+
+ // TODO remove code block for workaround in #10303
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
+ mCall.enqueue(this);
+ } else {
+ // Calling execute instead of enqueue is a workaround for #10303
+ onResponse(mCall, mCall.execute());
+ }
} catch (Exception exception) {
onFailure(exception);
}
@@ -109,13 +117,11 @@ class HTTPRequest implements Callback {
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
- Timber.v(String.format("[HTTP] Request was successful (code = %d).", response.code()));
+ Timber.v("[HTTP] Request was successful (code = %s).", response.code());
} else {
// We don't want to call this unsuccessful because a 304 isn't really an error
String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information";
- Timber.d(String.format(
- "[HTTP] Request with response code = %d: %s",
- response.code(), message));
+ Timber.d("[HTTP] Request with response code = %s: %s", response.code(), message);
}
byte[] body;
@@ -160,15 +166,12 @@ class HTTPRequest implements Callback {
String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request";
if (type == TEMPORARY_ERROR) {
- Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE,
- "Request failed due to a temporary error: %s", errorMessage));
+ Timber.d("Request failed due to a temporary error: %s", errorMessage);
} else if (type == CONNECTION_ERROR) {
- Timber.i(String.format(MapboxConstants.MAPBOX_LOCALE,
- "Request failed due to a connection error: %s", errorMessage));
+ Timber.i("Request failed due to a connection error: %s", errorMessage);
} else {
// PERMANENT_ERROR
- Timber.w(String.format(MapboxConstants.MAPBOX_LOCALE,
- "Request failed due to a permanent error: %s", errorMessage));
+ Timber.w("Request failed due to a permanent error: %s", errorMessage);
}
mLock.lock();
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
index 4e934fa3cc..c6bc13f538 100644
--- 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
@@ -2,22 +2,19 @@ package com.mapbox.mapboxsdk.location;
import android.content.Context;
import android.location.Location;
-import android.support.annotation.NonNull;
+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.mapbox.services.android.telemetry.permissions.PermissionsManager;
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;
-import java.lang.ref.WeakReference;
-
-import timber.log.Timber;
-
/**
+ * 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)
@@ -31,35 +28,31 @@ import timber.log.Timber;
* 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.
*/
-public class LocationSource extends LocationEngine implements
- LostApiClient.ConnectionCallbacks, LocationListener {
-
- private static LocationEngine instance;
+@Deprecated
+public class LocationSource extends LocationEngine implements LocationListener {
- private WeakReference<Context> context;
+ private Context context;
private LostApiClient lostApiClient;
- private LocationSource(Context context) {
+ /**
+ * Constructs a location source instance.
+ *
+ * @param context the context from which the Application context will be derived.
+ */
+ public LocationSource(Context context) {
super();
- this.context = new WeakReference<>(context);
- lostApiClient = new LostApiClient.Builder(this.context.get())
- .addConnectionCallbacks(this)
- .build();
+ this.context = context.getApplicationContext();
+ lostApiClient = new LostApiClient.Builder(this.context).build();
}
/**
- * Get the LocationEngine instance.
- *
- * @param context a Context from which the application context is derived
- * @return the LocationEngine instance
+ * Constructs a location source instance.
+ * Needed to create empty location source implementations.
*/
- public static synchronized LocationEngine getLocationEngine(@NonNull Context context) {
- if (instance == null) {
- instance = new LocationSource(context.getApplicationContext());
- }
-
- return instance;
+ public LocationSource() {
}
/**
@@ -68,9 +61,12 @@ public class LocationSource extends LocationEngine implements
*/
@Override
public void activate() {
- if (lostApiClient != null && !lostApiClient.isConnected()) {
+ if (!lostApiClient.isConnected()) {
lostApiClient.connect();
}
+ for (LocationEngineListener listener : locationListeners) {
+ listener.onConnected();
+ }
}
/**
@@ -80,7 +76,7 @@ public class LocationSource extends LocationEngine implements
*/
@Override
public void deactivate() {
- if (lostApiClient != null && lostApiClient.isConnected()) {
+ if (lostApiClient.isConnected()) {
lostApiClient.disconnect();
}
}
@@ -97,35 +93,17 @@ public class LocationSource extends LocationEngine implements
}
/**
- * 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() {
- Timber.d("Connection suspended.");
- }
-
- /**
* 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() && PermissionsManager.areLocationPermissionsGranted(context.get())) {
+ if (lostApiClient.isConnected()) {
//noinspection MissingPermission
- return LocationServices.FusedLocationApi.getLastLocation(lostApiClient);
+ return LocationServices.FusedLocationApi.getLastLocation();
}
-
return null;
}
@@ -134,13 +112,18 @@ public class LocationSource extends LocationEngine implements
*/
@Override
public void requestLocationUpdates() {
- // Common params
- LocationRequest request = LocationRequest.create()
- .setInterval(1000)
- .setFastestInterval(1000)
- .setSmallestDisplacement(3.0f);
+ LocationRequest request = LocationRequest.create();
+
+ if (interval != null) {
+ request.setInterval(interval);
+ }
+ if (fastestInterval != null) {
+ request.setFastestInterval(fastestInterval);
+ }
+ if (smallestDisplacement != null) {
+ request.setSmallestDisplacement(smallestDisplacement);
+ }
- // Priority matching is straightforward
if (priority == LocationEnginePriority.NO_POWER) {
request.setPriority(LocationRequest.PRIORITY_NO_POWER);
} else if (priority == LocationEnginePriority.LOW_POWER) {
@@ -151,9 +134,9 @@ public class LocationSource extends LocationEngine implements
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
- if (lostApiClient.isConnected() && PermissionsManager.areLocationPermissionsGranted(context.get())) {
+ if (lostApiClient.isConnected()) {
//noinspection MissingPermission
- LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this);
+ LocationServices.FusedLocationApi.requestLocationUpdates(request, this);
}
}
@@ -163,11 +146,21 @@ public class LocationSource extends LocationEngine implements
@Override
public void removeLocationUpdates() {
if (lostApiClient.isConnected()) {
- LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this);
+ LocationServices.FusedLocationApi.removeLocationUpdates(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
@@ -178,4 +171,4 @@ public class LocationSource extends LocationEngine implements
listener.onLocationChanged(location);
}
}
-}
+} \ 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
index 6b0c9a76c0..b27559e95e 100644
--- 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
@@ -1,4 +1,4 @@
/**
* Contains the Mapbox Maps Android Location API classes.
*/
-package com.mapbox.mapboxsdk.location;
+package com.mapbox.mapboxsdk.location; \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
index 7e7947047e..9f256c341b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
@@ -1,11 +1,16 @@
package com.mapbox.mapboxsdk.maps;
+import android.graphics.Bitmap;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.LongSparseArray;
+import android.view.View;
+import com.mapbox.mapboxsdk.Mapbox;
+import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
@@ -18,9 +23,10 @@ import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import timber.log.Timber;
+
/**
* Responsible for managing and tracking state of Annotations linked to Map. All events related to
* annotations that occur on {@link MapboxMap} are forwarded to this class.
@@ -34,7 +40,8 @@ import java.util.List;
*/
class AnnotationManager {
- private final NativeMapView nativeMapView;
+ private static final long NO_ANNOTATION_ID = -1;
+
private final MapView mapView;
private final IconManager iconManager;
private final InfoWindowManager infoWindowManager = new InfoWindowManager();
@@ -44,15 +51,18 @@ class AnnotationManager {
private MapboxMap mapboxMap;
private MapboxMap.OnMarkerClickListener onMarkerClickListener;
+ private MapboxMap.OnPolygonClickListener onPolygonClickListener;
+ private MapboxMap.OnPolylineClickListener onPolylineClickListener;
+
private Annotations annotations;
+ private ShapeAnnotations shapeAnnotations;
private Markers markers;
private Polygons polygons;
private Polylines polylines;
AnnotationManager(NativeMapView view, MapView mapView, LongSparseArray<Annotation> annotationsArray,
MarkerViewManager markerViewManager, IconManager iconManager, Annotations annotations,
- Markers markers, Polygons polygons, Polylines polylines) {
- this.nativeMapView = view;
+ Markers markers, Polygons polygons, Polylines polylines, ShapeAnnotations shapeAnnotations) {
this.mapView = mapView;
this.annotationsArray = annotationsArray;
this.markerViewManager = markerViewManager;
@@ -61,9 +71,10 @@ class AnnotationManager {
this.markers = markers;
this.polygons = polygons;
this.polylines = polylines;
+ this.shapeAnnotations = shapeAnnotations;
if (view != null) {
// null checking needed for unit tests
- nativeMapView.addOnMapChangedListener(markerViewManager);
+ view.addOnMapChangedListener(markerViewManager);
}
}
@@ -106,6 +117,9 @@ class AnnotationManager {
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ // do icon cleanup
+ iconManager.iconCleanup(marker.getIcon());
}
}
annotations.removeBy(annotation);
@@ -122,6 +136,8 @@ class AnnotationManager {
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ iconManager.iconCleanup(marker.getIcon());
}
}
}
@@ -141,6 +157,8 @@ class AnnotationManager {
marker.hideInfoWindow();
if (marker instanceof MarkerView) {
markerViewManager.removeMarkerView((MarkerView) marker);
+ } else {
+ iconManager.iconCleanup(marker.getIcon());
}
}
}
@@ -160,6 +178,10 @@ class AnnotationManager {
}
void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) {
+ if (!isAddedToMap(updatedMarker)) {
+ logNonAdded(updatedMarker);
+ return;
+ }
markers.update(updatedMarker, mapboxMap);
}
@@ -203,6 +225,10 @@ class AnnotationManager {
}
void updatePolygon(Polygon polygon) {
+ if (!isAddedToMap(polygon)) {
+ logNonAdded(polygon);
+ return;
+ }
polygons.update(polygon);
}
@@ -223,6 +249,10 @@ class AnnotationManager {
}
void updatePolyline(Polyline polyline) {
+ if (!isAddedToMap(polyline)) {
+ logNonAdded(polyline);
+ return;
+ }
polylines.update(polyline);
}
@@ -231,11 +261,18 @@ class AnnotationManager {
}
// TODO Refactor from here still in progress
-
void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) {
onMarkerClickListener = listener;
}
+ void setOnPolygonClickListener(@Nullable MapboxMap.OnPolygonClickListener listener) {
+ onPolygonClickListener = listener;
+ }
+
+ void setOnPolylineClickListener(@Nullable MapboxMap.OnPolylineClickListener listener) {
+ onPolylineClickListener = listener;
+ }
+
void selectMarker(@NonNull Marker marker) {
if (selectedMarkers.contains(marker)) {
return;
@@ -325,88 +362,203 @@ class AnnotationManager {
}
}
+ private boolean isAddedToMap(Annotation annotation) {
+ return annotation != null && annotation.getId() != -1 && annotationsArray.indexOfKey(annotation.getId()) > -1;
+ }
+
+ private void logNonAdded(Annotation annotation) {
+ Timber.w("Attempting to update non-added %s with value %s", annotation.getClass().getCanonicalName(), annotation);
+ }
+
//
// Click event
//
- boolean onTap(PointF tapPoint, float screenDensity) {
- float toleranceSides = 4 * screenDensity;
- float toleranceTopBottom = 10 * screenDensity;
- boolean handledDefaultClick = false;
-
- RectF tapRect = new RectF(tapPoint.x - iconManager.getAverageIconWidth() / 2 - toleranceSides,
- tapPoint.y - iconManager.getAverageIconHeight() / 2 - toleranceTopBottom,
- tapPoint.x + iconManager.getAverageIconWidth() / 2 + toleranceSides,
- tapPoint.y + iconManager.getAverageIconHeight() / 2 + toleranceTopBottom);
-
- List<Marker> nearbyMarkers = getMarkersInRect(tapRect);
- long newSelectedMarkerId = -1;
-
- // find a Marker that isn't selected yet
- if (nearbyMarkers.size() > 0) {
- Collections.sort(nearbyMarkers);
- for (Marker nearbyMarker : nearbyMarkers) {
- boolean found = false;
- for (Marker selectedMarker : selectedMarkers) {
- if (selectedMarker.equals(nearbyMarker)) {
- found = true;
- }
- }
- if (!found) {
- newSelectedMarkerId = nearbyMarker.getId();
- break;
- }
+ boolean onTap(PointF tapPoint) {
+ ShapeAnnotationHit shapeAnnotationHit = getShapeAnnotationHitFromTap(tapPoint);
+ Annotation annotation = new ShapeAnnotationHitResolver(shapeAnnotations).execute(shapeAnnotationHit);
+ if (annotation != null) {
+ if (handleClickForShapeAnnotation(annotation)) {
+ return true;
+ }
+ }
+
+ MarkerHit markerHit = getMarkerHitFromTouchArea(tapPoint);
+ long markerId = new MarkerHitResolver(mapboxMap).execute(markerHit);
+ return markerId != NO_ANNOTATION_ID && isClickHandledForMarker(markerId);
+ }
+
+ private ShapeAnnotationHit getShapeAnnotationHitFromTap(PointF tapPoint) {
+ float touchTargetSide = Mapbox.getApplicationContext().getResources().getDimension(R.dimen.mapbox_eight_dp);
+ RectF tapRect = new RectF(
+ tapPoint.x - touchTargetSide,
+ tapPoint.y - touchTargetSide,
+ tapPoint.x + touchTargetSide,
+ tapPoint.y + touchTargetSide
+ );
+ return new ShapeAnnotationHit(tapRect);
+ }
+
+ private boolean handleClickForShapeAnnotation(Annotation annotation) {
+ if (annotation instanceof Polygon && onPolygonClickListener != null) {
+ onPolygonClickListener.onPolygonClick((Polygon) annotation);
+ return true;
+ } else if (annotation instanceof Polyline && onPolylineClickListener != null) {
+ onPolylineClickListener.onPolylineClick((Polyline) annotation);
+ return true;
+ }
+ return false;
+ }
+
+ private MarkerHit getMarkerHitFromTouchArea(PointF tapPoint) {
+ int touchSurfaceWidth = (int) (iconManager.getHighestIconHeight() * 1.5);
+ int touchSurfaceHeight = (int) (iconManager.getHighestIconWidth() * 1.5);
+ final RectF tapRect = new RectF(tapPoint.x - touchSurfaceWidth,
+ tapPoint.y - touchSurfaceHeight,
+ tapPoint.x + touchSurfaceWidth,
+ tapPoint.y + touchSurfaceHeight
+ );
+ return new MarkerHit(tapRect, getMarkersInRect(tapRect));
+ }
+
+ private boolean isClickHandledForMarker(long markerId) {
+ boolean handledDefaultClick;
+ Marker marker = (Marker) getAnnotation(markerId);
+ if (marker instanceof MarkerView) {
+ handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) marker);
+ } else {
+ handledDefaultClick = onClickMarker(marker);
+ }
+
+ if (!handledDefaultClick) {
+ toggleMarkerSelectionState(marker);
+ }
+ return true;
+ }
+
+ private boolean onClickMarker(Marker marker) {
+ return onMarkerClickListener != null && onMarkerClickListener.onMarkerClick(marker);
+ }
+
+ private void toggleMarkerSelectionState(Marker marker) {
+ if (!selectedMarkers.contains(marker)) {
+ selectMarker(marker);
+ } else {
+ deselectMarker(marker);
+ }
+ }
+
+ private static class ShapeAnnotationHitResolver {
+
+ private ShapeAnnotations shapeAnnotations;
+
+ ShapeAnnotationHitResolver(ShapeAnnotations shapeAnnotations) {
+ this.shapeAnnotations = shapeAnnotations;
+ }
+
+ public Annotation execute(ShapeAnnotationHit shapeHit) {
+ Annotation foundAnnotation = null;
+ List<Annotation> annotations = shapeAnnotations.obtainAllIn(shapeHit.tapPoint);
+ if (annotations.size() > 0) {
+ foundAnnotation = annotations.get(0);
}
+ return foundAnnotation;
+ }
+ }
+
+ private static class MarkerHitResolver {
+
+ private final MarkerViewManager markerViewManager;
+ private final Projection projection;
+
+ private View view;
+ private Bitmap bitmap;
+ private PointF markerLocation;
+
+ private Rect hitRectView = new Rect();
+ private RectF hitRectMarker = new RectF();
+ private RectF highestSurfaceIntersection = new RectF();
+
+ private long closestMarkerId = NO_ANNOTATION_ID;
+
+ MarkerHitResolver(@NonNull MapboxMap mapboxMap) {
+ this.markerViewManager = mapboxMap.getMarkerViewManager();
+ this.projection = mapboxMap.getProjection();
+ }
+
+ public long execute(MarkerHit markerHit) {
+ resolveForMarkers(markerHit);
+ return closestMarkerId;
}
- // if unselected marker found
- if (newSelectedMarkerId >= 0) {
- List<Annotation> annotations = getAnnotations();
- int count = annotations.size();
- for (int i = 0; i < count; i++) {
- Annotation annotation = annotations.get(i);
- if (annotation instanceof Marker) {
- if (annotation.getId() == newSelectedMarkerId) {
- Marker marker = (Marker) annotation;
-
- if (marker instanceof MarkerView) {
- handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) marker);
- } else {
- if (onMarkerClickListener != null) {
- // end developer has provided a custom click listener
- handledDefaultClick = onMarkerClickListener.onMarkerClick(marker);
- }
- }
-
- if (!handledDefaultClick) {
- // only select marker if user didn't handle the click event themselves
- selectMarker(marker);
- }
-
- return true;
- }
+ private void resolveForMarkers(MarkerHit markerHit) {
+ for (Marker marker : markerHit.markers) {
+ if (marker instanceof MarkerView) {
+ resolveForMarkerView(markerHit, (MarkerView) marker);
+ } else {
+ resolveForMarker(markerHit, marker);
}
}
- } else if (nearbyMarkers.size() > 0) {
- // we didn't find an unselected marker, check if we can close an already open markers
- for (Marker nearbyMarker : nearbyMarkers) {
- for (Marker selectedMarker : selectedMarkers) {
- if (nearbyMarker.equals(selectedMarker)) {
- if (nearbyMarker instanceof MarkerView) {
- handledDefaultClick = markerViewManager.onClickMarkerView((MarkerView) nearbyMarker);
- } else if (onMarkerClickListener != null) {
- handledDefaultClick = onMarkerClickListener.onMarkerClick(nearbyMarker);
- }
-
- if (!handledDefaultClick) {
- // only deselect marker if user didn't handle the click event themselves
- deselectMarker(nearbyMarker);
- }
- return true;
- }
+ }
+
+ private void resolveForMarkerView(MarkerHit markerHit, MarkerView markerView) {
+ view = markerViewManager.getView(markerView);
+ if (view != null) {
+ view.getHitRect(hitRectView);
+ hitRectMarker = new RectF(hitRectView);
+ hitTestMarker(markerHit, markerView, hitRectMarker);
+ }
+ }
+
+ private void resolveForMarker(MarkerHit markerHit, Marker marker) {
+ markerLocation = projection.toScreenLocation(marker.getPosition());
+ bitmap = marker.getIcon().getBitmap();
+ hitRectMarker.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ hitRectMarker.offsetTo(
+ markerLocation.x - bitmap.getWidth() / 2,
+ markerLocation.y - bitmap.getHeight() / 2
+ );
+ hitTestMarker(markerHit, marker, hitRectMarker);
+ }
+
+ private void hitTestMarker(MarkerHit markerHit, Marker marker, RectF hitRectMarker) {
+ if (hitRectMarker.contains(markerHit.getTapPointX(), markerHit.getTapPointY())) {
+ hitRectMarker.intersect(markerHit.tapRect);
+ if (isRectangleHighestSurfaceIntersection(hitRectMarker)) {
+ highestSurfaceIntersection = new RectF(hitRectMarker);
+ closestMarkerId = marker.getId();
}
}
}
- return false;
+
+ private boolean isRectangleHighestSurfaceIntersection(RectF rectF) {
+ return rectF.width() * rectF.height() > highestSurfaceIntersection.width() * highestSurfaceIntersection.height();
+ }
+ }
+
+ private static class ShapeAnnotationHit {
+ private final RectF tapPoint;
+
+ ShapeAnnotationHit(RectF tapPoint) {
+ this.tapPoint = tapPoint;
+ }
+ }
+
+ private static class MarkerHit {
+ private final RectF tapRect;
+ private final List<Marker> markers;
+
+ MarkerHit(RectF tapRect, List<Marker> markers) {
+ this.tapRect = tapRect;
+ this.markers = markers;
+ }
+
+ float getTapPointX() {
+ return tapRect.centerX();
+ }
+
+ float getTapPointY() {
+ return tapRect.centerY();
+ }
}
}
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 5113a0cc73..9ccff387f5 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
@@ -1,12 +1,12 @@
package com.mapbox.mapboxsdk.maps;
+import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
-import android.support.v7.app.AlertDialog;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -56,7 +56,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
private void showAttributionDialog() {
attributionKeys = attributionMap.keySet().toArray(new String[attributionMap.size()]);
- AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.mapbox_attributionsDialogTitle);
builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionKeys), this);
builder.show();
@@ -77,7 +77,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
}
private void showTelemetryDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.mapbox_attributionTelemetryTitle);
builder.setMessage(R.string.mapbox_attributionTelemetryMessage);
builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java
index bd028aecb6..f046744c31 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java
@@ -1,32 +1,45 @@
package com.mapbox.mapboxsdk.maps;
+import android.support.annotation.NonNull;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener;
-import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener;
+import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener;
class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener,
MapboxMap.OnCameraMoveCanceledListener, OnCameraIdleListener {
private boolean idle = true;
+ private final CopyOnWriteArrayList<OnCameraMoveStartedListener> onCameraMoveStarted = new CopyOnWriteArrayList<>();
+ private final CopyOnWriteArrayList<OnCameraMoveCanceledListener> onCameraMoveCanceled = new CopyOnWriteArrayList<>();
+ private final CopyOnWriteArrayList<OnCameraMoveListener> onCameraMove = new CopyOnWriteArrayList<>();
+ private final CopyOnWriteArrayList<OnCameraIdleListener> onCameraIdle = new CopyOnWriteArrayList<>();
+
private OnCameraMoveStartedListener onCameraMoveStartedListener;
private OnCameraMoveCanceledListener onCameraMoveCanceledListener;
private OnCameraMoveListener onCameraMoveListener;
private OnCameraIdleListener onCameraIdleListener;
+ @Deprecated
void setOnCameraMoveStartedListener(OnCameraMoveStartedListener onCameraMoveStartedListener) {
this.onCameraMoveStartedListener = onCameraMoveStartedListener;
}
+ @Deprecated
void setOnCameraMoveCanceledListener(OnCameraMoveCanceledListener onCameraMoveCanceledListener) {
this.onCameraMoveCanceledListener = onCameraMoveCanceledListener;
}
+ @Deprecated
void setOnCameraMoveListener(OnCameraMoveListener onCameraMoveListener) {
this.onCameraMoveListener = onCameraMoveListener;
}
+ @Deprecated
void setOnCameraIdleListener(OnCameraIdleListener onCameraIdleListener) {
this.onCameraIdleListener = onCameraIdleListener;
}
@@ -36,32 +49,106 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
if (!idle) {
return;
}
-
idle = false;
+
+ // deprecated API
if (onCameraMoveStartedListener != null) {
onCameraMoveStartedListener.onCameraMoveStarted(reason);
}
+
+ // new API
+ if (!onCameraMoveStarted.isEmpty()) {
+ for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStarted) {
+ cameraMoveStartedListener.onCameraMoveStarted(reason);
+ }
+ }
}
@Override
public void onCameraMove() {
+ // deprecated API
if (onCameraMoveListener != null && !idle) {
onCameraMoveListener.onCameraMove();
}
+
+ // new API
+ if (!onCameraMove.isEmpty() && !idle) {
+ for (OnCameraMoveListener cameraMoveListener : onCameraMove) {
+ cameraMoveListener.onCameraMove();
+ }
+ }
}
@Override
public void onCameraMoveCanceled() {
+ // deprecated API
if (onCameraMoveCanceledListener != null && !idle) {
onCameraMoveCanceledListener.onCameraMoveCanceled();
}
+
+ // new API
+ if (!onCameraMoveCanceled.isEmpty() && !idle) {
+ for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceled) {
+ cameraMoveCanceledListener.onCameraMoveCanceled();
+ }
+ }
}
@Override
public void onCameraIdle() {
- if (onCameraIdleListener != null && !idle) {
+ if (!idle) {
idle = true;
- onCameraIdleListener.onCameraIdle();
+ // deprecated API
+ if (onCameraIdleListener != null) {
+ onCameraIdleListener.onCameraIdle();
+ }
+
+ // new API
+ if (!onCameraIdle.isEmpty()) {
+ for (OnCameraIdleListener cameraIdleListener : onCameraIdle) {
+ cameraIdleListener.onCameraIdle();
+ }
+ }
+ }
+ }
+
+ void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
+ onCameraIdle.add(listener);
+ }
+
+ void removeOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
+ if (onCameraIdle.contains(listener)) {
+ onCameraIdle.remove(listener);
+ }
+ }
+
+ void addOnCameraMoveCancelListener(OnCameraMoveCanceledListener listener) {
+ onCameraMoveCanceled.add(listener);
+ }
+
+ void removeOnCameraMoveCancelListener(OnCameraMoveCanceledListener listener) {
+ if (onCameraMoveCanceled.contains(listener)) {
+ onCameraMoveCanceled.remove(listener);
+ }
+ }
+
+ void addOnCameraMoveStartedListener(OnCameraMoveStartedListener listener) {
+ onCameraMoveStarted.add(listener);
+ }
+
+ void removeOnCameraMoveStartedListener(OnCameraMoveStartedListener listener) {
+ if (onCameraMoveStarted.contains(listener)) {
+ onCameraMoveStarted.remove(listener);
+ }
+ }
+
+ void addOnCameraMoveListener(OnCameraMoveListener listener) {
+ onCameraMove.add(listener);
+ }
+
+ void removeOnCameraMoveListener(OnCameraMoveListener listener) {
+ if (onCameraMove.contains(listener)) {
+ onCameraMove.remove(listener);
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
index 9f4171aee8..80ffa973e7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
@@ -7,10 +7,10 @@ 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.exceptions.IconBitmapChangedException;
-import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Responsible for managing icons added to the Map.
@@ -25,15 +25,14 @@ import java.util.List;
*/
class IconManager {
- private NativeMapView nativeMapView;
- private List<Icon> icons;
+ private final Map<Icon, Integer> iconMap = new HashMap<>();
- private int averageIconHeight;
- private int averageIconWidth;
+ private NativeMapView nativeMapView;
+ private int highestIconWidth;
+ private int highestIconHeight;
IconManager(NativeMapView nativeMapView) {
this.nativeMapView = nativeMapView;
- this.icons = new ArrayList<>();
// load transparent icon for MarkerView to trace actual markers, see #6352
loadIcon(IconFactory.recreate(IconFactory.ICON_MARKERVIEW_ID, IconFactory.ICON_MARKERVIEW_BITMAP));
}
@@ -45,7 +44,7 @@ class IconManager {
// TODO we can move this code afterwards to getIcon as with MarkerView.getIcon
icon = loadDefaultIconForMarker(marker);
} else {
- updateAverageIconSize(icon);
+ updateHighestIconSize(icon);
}
addIcon(icon);
return icon;
@@ -54,7 +53,7 @@ class IconManager {
void loadIconForMarkerView(MarkerView marker) {
Icon icon = marker.getIcon();
Bitmap bitmap = icon.getBitmap();
- updateAverageIconSize(bitmap);
+ updateHighestIconSize(bitmap);
addIcon(icon, false);
}
@@ -62,18 +61,18 @@ class IconManager {
return (int) (nativeMapView.getTopOffsetPixelsForAnnotationSymbol(icon.getId()) * nativeMapView.getPixelRatio());
}
- int getAverageIconHeight() {
- return averageIconHeight;
+ int getHighestIconWidth() {
+ return highestIconWidth;
}
- int getAverageIconWidth() {
- return averageIconWidth;
+ int getHighestIconHeight() {
+ return highestIconHeight;
}
private Icon loadDefaultIconForMarker(Marker marker) {
Icon icon = IconFactory.getInstance(Mapbox.getApplicationContext()).defaultMarker();
Bitmap bitmap = icon.getBitmap();
- updateAverageIconSize(bitmap.getWidth(), bitmap.getHeight() / 2);
+ updateHighestIconSize(bitmap.getWidth(), bitmap.getHeight() / 2);
marker.setIcon(icon);
return icon;
}
@@ -83,28 +82,32 @@ class IconManager {
}
private void addIcon(Icon icon, boolean addIconToMap) {
- if (!icons.contains(icon)) {
- icons.add(icon);
+ if (!iconMap.keySet().contains(icon)) {
+ iconMap.put(icon, 1);
if (addIconToMap) {
loadIcon(icon);
}
} else {
- validateIconChanged(icon);
+ iconMap.put(icon, iconMap.get(icon) + 1);
}
}
- private void updateAverageIconSize(Icon icon) {
- updateAverageIconSize(icon.getBitmap());
+ private void updateHighestIconSize(Icon icon) {
+ updateHighestIconSize(icon.getBitmap());
}
- private void updateAverageIconSize(Bitmap bitmap) {
- updateAverageIconSize(bitmap.getWidth(), bitmap.getHeight());
+ private void updateHighestIconSize(Bitmap bitmap) {
+ updateHighestIconSize(bitmap.getWidth(), bitmap.getHeight());
}
- private void updateAverageIconSize(int width, int height) {
- int iconSize = icons.size() + 1;
- averageIconHeight = averageIconHeight + (height - averageIconHeight) / iconSize;
- averageIconWidth = averageIconWidth + (width - averageIconWidth) / iconSize;
+ private void updateHighestIconSize(int width, int height) {
+ if (width > highestIconWidth) {
+ highestIconWidth = width;
+ }
+
+ if (height > highestIconHeight) {
+ highestIconHeight = height;
+ }
}
private void loadIcon(Icon icon) {
@@ -117,18 +120,11 @@ class IconManager {
}
void reloadIcons() {
- for (Icon icon : icons) {
+ for (Icon icon : iconMap.keySet()) {
loadIcon(icon);
}
}
- private void validateIconChanged(Icon icon) {
- Icon oldIcon = icons.get(icons.indexOf(icon));
- if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) {
- throw new IconBitmapChangedException();
- }
- }
-
void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) {
Icon icon = marker.getIcon();
if (icon == null) {
@@ -145,4 +141,26 @@ class IconManager {
marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon));
}
}
+
+ void iconCleanup(Icon icon) {
+ Integer refCounter = iconMap.get(icon);
+ if (refCounter != null) {
+ refCounter--;
+ if (refCounter == 0) {
+ remove(icon);
+ } else {
+ updateIconRefCounter(icon, refCounter);
+ }
+ }
+ }
+
+ private void remove(Icon icon) {
+ nativeMapView.removeAnnotationIcon(icon.getId());
+ iconMap.remove(icon);
+ }
+
+ private void updateIconRefCounter(Icon icon, int refCounter) {
+ iconMap.put(icon, refCounter);
+ }
+
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java
new file mode 100644
index 0000000000..b2f6cef3b0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java
@@ -0,0 +1,17 @@
+package com.mapbox.mapboxsdk.maps;
+
+class Image {
+ private final byte[] buffer;
+ private final float pixelRatio;
+ private final String name;
+ private final int width;
+ private final int height;
+
+ public Image(byte[] buffer, float pixelRatio, String name, int width, int height) {
+ this.buffer = buffer;
+ this.pixelRatio = pixelRatio;
+ this.name = name;
+ this.width = width;
+ this.height = height;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java
index 7599b6afa6..af207204d9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java
@@ -1,6 +1,5 @@
package com.mapbox.mapboxsdk.maps;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
@@ -22,7 +21,8 @@ import java.util.List;
*/
class InfoWindowManager {
- private List<InfoWindow> infoWindows;
+ private final List<InfoWindow> infoWindows = new ArrayList<>();
+
private MapboxMap.InfoWindowAdapter infoWindowAdapter;
private boolean allowConcurrentMultipleInfoWindows;
@@ -30,13 +30,11 @@ class InfoWindowManager {
private MapboxMap.OnInfoWindowLongClickListener onInfoWindowLongClickListener;
private MapboxMap.OnInfoWindowCloseListener onInfoWindowCloseListener;
- InfoWindowManager() {
- this.infoWindows = new ArrayList<>();
- }
-
void update() {
- for (InfoWindow infoWindow : infoWindows) {
- infoWindow.update();
+ if (!infoWindows.isEmpty()) {
+ for (InfoWindow infoWindow : infoWindows) {
+ infoWindow.update();
+ }
}
}
@@ -56,12 +54,8 @@ class InfoWindowManager {
return allowConcurrentMultipleInfoWindows;
}
- List<InfoWindow> getInfoWindows() {
- return infoWindows;
- }
-
- boolean isInfoWindowValidForMarker(@NonNull Marker marker) {
- return !TextUtils.isEmpty(marker.getTitle()) || !TextUtils.isEmpty(marker.getSnippet());
+ boolean isInfoWindowValidForMarker(Marker marker) {
+ return marker != null && (!TextUtils.isEmpty(marker.getTitle()) || !TextUtils.isEmpty(marker.getSnippet()));
}
void setOnInfoWindowClickListener(@Nullable MapboxMap.OnInfoWindowClickListener listener) {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java
index 8b1ba7b771..01c6da4971 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java
@@ -76,6 +76,9 @@ public final class MapFragment extends Fragment {
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
map.onCreate(savedInstanceState);
+ if (onMapReadyCallback != null) {
+ map.getMapAsync(onMapReadyCallback);
+ }
}
/**
@@ -85,7 +88,6 @@ public final class MapFragment extends Fragment {
public void onStart() {
super.onStart();
map.onStart();
- map.getMapAsync(onMapReadyCallback);
}
/**
@@ -150,6 +152,10 @@ public final class MapFragment extends Fragment {
* @param onMapReadyCallback The callback to be invoked.
*/
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
- this.onMapReadyCallback = onMapReadyCallback;
+ if (map == null) {
+ this.onMapReadyCallback = onMapReadyCallback;
+ } else {
+ map.getMapAsync(onMapReadyCallback);
+ }
}
}
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 33e13c5ecc..0fea5ce0ff 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
@@ -1,15 +1,19 @@
package com.mapbox.mapboxsdk.maps;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.location.Location;
-import android.support.annotation.NonNull;
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;
@@ -22,6 +26,8 @@ import com.mapbox.services.android.telemetry.MapboxTelemetry;
import com.mapbox.services.android.telemetry.utils.MathUtils;
import com.mapbox.services.android.telemetry.utils.TelemetryUtils;
+import java.util.concurrent.CopyOnWriteArrayList;
+
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE;
/**
@@ -39,24 +45,46 @@ final class MapGestureDetector {
private final AnnotationManager annotationManager;
private final CameraChangeDispatcher cameraChangeDispatcher;
- private final GestureDetectorCompat gestureDetector;
- private final ScaleGestureDetector scaleGestureDetector;
- private final RotateGestureDetector rotateGestureDetector;
- private final ShoveGestureDetector shoveGestureDetector;
+ private GestureDetectorCompat gestureDetector;
+ private ScaleGestureDetector scaleGestureDetector;
+ private RotateGestureDetector rotateGestureDetector;
+ private ShoveGestureDetector shoveGestureDetector;
+ // deprecated map touch API
private MapboxMap.OnMapClickListener onMapClickListener;
private MapboxMap.OnMapLongClickListener onMapLongClickListener;
private MapboxMap.OnFlingListener onFlingListener;
private MapboxMap.OnScrollListener onScrollListener;
+ // new map touch API
+ private final CopyOnWriteArrayList<MapboxMap.OnMapClickListener> onMapClickListenerList
+ = new CopyOnWriteArrayList<>();
+
+ private final CopyOnWriteArrayList<MapboxMap.OnMapLongClickListener> onMapLongClickListenerList
+ = new CopyOnWriteArrayList<>();
+
+ private final CopyOnWriteArrayList<MapboxMap.OnFlingListener> onFlingListenerList
+ = new CopyOnWriteArrayList<>();
+
+ private final CopyOnWriteArrayList<MapboxMap.OnScrollListener> onScrollListenerList
+ = new CopyOnWriteArrayList<>();
+
private PointF focalPoint;
- private boolean twoTap = false;
- private boolean zoomStarted = false;
- private boolean dragStarted = false;
- private boolean quickZoom = false;
- private boolean scrollInProgress = false;
- private boolean scaleGestureOccurred = false;
+ private boolean twoTap;
+ private boolean quickZoom;
+ private boolean tiltGestureOccurred;
+ private boolean scrollGestureOccurred;
+
+ private boolean scaleGestureOccurred;
+ private boolean recentScaleGestureOccurred;
+ private boolean scaleAnimating;
+ private long scaleBeginTime;
+
+ private VelocityTracker velocityTracker;
+ private boolean wasZoomingIn;
+ private boolean wasClockwiseRotating;
+ private boolean rotateGestureOccurred;
MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings,
TrackingSettings trackingSettings, AnnotationManager annotationManager,
@@ -69,12 +97,14 @@ final class MapGestureDetector {
this.cameraChangeDispatcher = cameraChangeDispatcher;
// Touch gesture detectors
- 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());
+ 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());
+ }
}
/**
@@ -133,22 +163,33 @@ final class MapGestureDetector {
* @param event the MotionEvent
* @return True if touch event is handled
*/
- boolean onTouchEvent(@NonNull MotionEvent event) {
+ boolean onTouchEvent(MotionEvent event) {
+ // framework can return null motion events in edge cases #9432
+ if (event == null) {
+ return false;
+ }
+
// Check and ignore non touch or left clicks
if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
return false;
}
// Check two finger gestures first
- rotateGestureDetector.onTouchEvent(event);
scaleGestureDetector.onTouchEvent(event);
+ rotateGestureDetector.onTouchEvent(event);
shoveGestureDetector.onTouchEvent(event);
// Handle two finger tap
switch (event.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
- scaleGestureOccurred = false;
+ recentScaleGestureOccurred = false;
transform.setGestureInProgress(true);
break;
@@ -188,20 +229,34 @@ final class MapGestureDetector {
}
// Scroll / Pan Has Stopped
- if (scrollInProgress) {
+ if (scrollGestureOccurred) {
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent(
getLocationFromGesture(event.getX(), event.getY()), transform));
- scrollInProgress = false;
+ scrollGestureOccurred = false;
cameraChangeDispatcher.onCameraIdle();
}
twoTap = false;
transform.setGestureInProgress(false);
+ if (velocityTracker != null) {
+ velocityTracker.recycle();
+ }
+ velocityTracker = null;
break;
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;
}
@@ -250,7 +305,6 @@ final class MapGestureDetector {
return false;
}
-
/**
* Responsible for handling one finger gestures.
*/
@@ -274,7 +328,7 @@ final class MapGestureDetector {
break;
case MotionEvent.ACTION_UP:
if (quickZoom) {
- // insert here?
+ cameraChangeDispatcher.onCameraIdle();
quickZoom = false;
break;
}
@@ -310,7 +364,7 @@ final class MapGestureDetector {
@Override
public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
PointF tapPoint = new PointF(motionEvent.getX(), motionEvent.getY());
- boolean tapHandled = annotationManager.onTap(tapPoint, uiSettings.getPixelRatio());
+ boolean tapHandled = annotationManager.onTap(tapPoint);
if (!tapHandled) {
if (uiSettings.isDeselectMarkersOnTap()) {
@@ -318,10 +372,7 @@ final class MapGestureDetector {
annotationManager.deselectMarkers();
}
- // notify app of map click
- if (onMapClickListener != null) {
- onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint));
- }
+ notifyOnMapClickListeners(tapPoint);
}
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
@@ -333,20 +384,20 @@ final class MapGestureDetector {
@Override
public void onLongPress(MotionEvent motionEvent) {
- if (onMapLongClickListener != null && !quickZoom) {
- onMapLongClickListener.onMapLongClick(
- projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY())));
+ PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY());
+
+ if (!quickZoom) {
+ notifyOnMapLongClickListeners(longClickPoint);
}
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || scaleGestureOccurred) {
+ if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || recentScaleGestureOccurred) {
// don't allow a fling is scroll is disabled
// and ignore when a scale gesture has occurred
return false;
}
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
float screenDensity = uiSettings.getPixelRatio();
@@ -362,9 +413,11 @@ final class MapGestureDetector {
// cancel any animation
transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
// tilt results in a bigger translation, limiting input for #5281
double tilt = transform.getTilt();
- double tiltFactor = 1 + ((tilt != 0) ? (tilt / 10) : 0); /* 1 -> 7 */
+ double tiltFactor = 1.5 + ((tilt != 0) ? (tilt / 10) : 0);
double offsetX = velocityX / tiltFactor / screenDensity;
double offsetY = velocityY / tiltFactor / screenDensity;
@@ -374,9 +427,7 @@ final class MapGestureDetector {
// update transformation
transform.moveBy(offsetX, offsetY, animationTime);
- if (onFlingListener != null) {
- onFlingListener.onFling();
- }
+ notifyOnFlingListeners();
return true;
}
@@ -387,16 +438,19 @@ final class MapGestureDetector {
return false;
}
- if (dragStarted) {
+ if (tiltGestureOccurred) {
return false;
}
- if (!scrollInProgress) {
- scrollInProgress = true;
+ if (!scrollGestureOccurred) {
+ scrollGestureOccurred = true;
// Cancel any animation
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ if (!scaleGestureOccurred) {
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ }
+
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
getLocationFromGesture(e1.getX(), e1.getY()),
MapboxEvent.GESTURE_PAN_START, transform));
@@ -408,20 +462,69 @@ final class MapGestureDetector {
// Scroll the map
transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/);
- if (onScrollListener != null) {
- onScrollListener.onScroll();
- }
+ notifyOnScrollListeners();
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));
+ }
+ }
+
+ void notifyOnMapLongClickListeners(PointF longClickPoint) {
+ // deprecated API
+ if (onMapLongClickListener != null) {
+ onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
+ }
+
+ // new API
+ for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) {
+ listener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
+ }
+ }
+
+ void notifyOnFlingListeners() {
+ // deprecated API
+ if (onFlingListener != null) {
+ onFlingListener.onFling();
+ }
+
+ // new API
+ for (MapboxMap.OnFlingListener listener : onFlingListenerList) {
+ listener.onFling();
+ }
+ }
+
+ void notifyOnScrollListeners() {
+ //deprecated API
+ if (onScrollListener != null) {
+ onScrollListener.onScroll();
+ }
+
+ // new API
+ for (MapboxMap.OnScrollListener listener : onScrollListenerList) {
+ listener.onScroll();
+ }
+ }
+
/**
* Responsible for handling two finger gestures and double-tap drag gestures.
*/
private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
- long beginTime = 0;
- float scaleFactor = 1.0f;
+ private static final int ANIMATION_TIME_MULTIPLIER = 77;
+ private static final double ZOOM_DISTANCE_DIVIDER = 5;
+
+ private float scaleFactor = 1.0f;
+ private PointF scalePointBegin;
// Called when two fingers first touch the screen
@Override
@@ -430,22 +533,15 @@ final class MapGestureDetector {
return false;
}
- scaleGestureOccurred = true;
- beginTime = detector.getEventTime();
+ 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 when fingers leave screen
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- beginTime = 0;
- scaleFactor = 1.0f;
- zoomStarted = false;
- }
-
// Called each time a finger moves
// Called for pinch zooms and quickzooms/quickscales
@Override
@@ -454,56 +550,126 @@ final class MapGestureDetector {
return super.onScale(detector);
}
- // If scale is large enough ignore a tap
- scaleFactor *= detector.getScaleFactor();
- if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) {
- // notify camera change listener
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- zoomStarted = true;
+ wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) >= 0;
+ if (tiltGestureOccurred) {
+ return false;
}
// Ignore short touches in case it is a tap
// Also ignore small scales
long time = detector.getEventTime();
- long interval = time - beginTime;
- if (!zoomStarted && (interval <= ViewConfiguration.getTapTimeout())) {
+ long interval = time - scaleBeginTime;
+ if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
return false;
}
- if (!zoomStarted) {
- return false;
+ // 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;
}
- if (dragStarted) {
+ if (!scaleGestureOccurred) {
return false;
}
// Gesture is a quickzoom if there aren't two fingers
+ if (!quickZoom && !twoTap) {
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ }
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
if (focalPoint != null) {
// arround user provided focal point
- transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(2), focalPoint.x, focalPoint.y);
+ transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), focalPoint.x, focalPoint.y);
} else if (quickZoom) {
+ cameraChangeDispatcher.onCameraMove();
// clamp scale factors we feed to core #7514
- float scaleFactor = MathUtils.clamp(detector.getScaleFactor(),
+ 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);
- // around center map
- transform.zoomBy(Math.log(scaleFactor) / Math.log(2), uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
+ transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
+ recentScaleGestureOccurred = true;
} else {
// around gesture
- transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(2), detector.getFocusX(), detector.getFocusY());
+ transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2),
+ scalePointBegin.x, scalePointBegin.y);
}
-
return true;
}
+
+ // Called when fingers leave screen
+ @Override
+ public void onScaleEnd(final ScaleGestureDetector detector) {
+ if (velocityTracker == null) {
+ return;
+ }
+
+
+ if (rotateGestureOccurred || quickZoom) {
+ reset();
+ 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();
+ }
+ }
+
+ private void reset() {
+ scaleAnimating = false;
+ scaleGestureOccurred = false;
+ scaleBeginTime = 0;
+ scaleFactor = 1.0f;
+ cameraChangeDispatcher.onCameraIdle();
+ }
+
+ private double calculateScale(double velocityXY) {
+ double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER);
+ if (!wasZoomingIn) {
+ zoomAddition = -zoomAddition;
+ }
+ return zoomAddition;
+ }
+
+ private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) {
+ ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition));
+ animator.setDuration(animationTime);
+ animator.setInterpolator(new FastOutSlowInInterpolator());
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin);
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ reset();
+ }
+ });
+ return animator;
+ }
}
/**
@@ -511,9 +677,13 @@ final class MapGestureDetector {
*/
private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
- long beginTime = 0;
- float totalAngle = 0.0f;
- boolean started = false;
+ 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;
+ private boolean animating = false;
// Called when two fingers first touch the screen
@Override
@@ -526,54 +696,42 @@ final class MapGestureDetector {
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
beginTime = detector.getEventTime();
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_ROTATION_START, transform));
return true;
}
- // Called when the fingers leave the screen
- @Override
- public void onRotateEnd(RotateGestureDetector detector) {
- // notify camera change listener
- beginTime = 0;
- totalAngle = 0.0f;
- started = false;
- }
-
// Called each time one of the two fingers moves
// Called for rotation
@Override
public boolean onRotate(RotateGestureDetector detector) {
- if (!trackingSettings.isRotateGestureCurrentlyEnabled() || dragStarted) {
+ if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) {
return false;
}
// If rotate is large enough ignore a tap
// Also is zoom already started, don't rotate
- totalAngle += detector.getRotationDegreesDelta();
- if (!zoomStarted && ((totalAngle > 20.0f) || (totalAngle < -20.0f))) {
+ 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;
}
- // Ignore short touches in case it is a tap
- // Also ignore small rotate
- long time = detector.getEventTime();
- long interval = time - beginTime;
- if (!started && (interval <= ViewConfiguration.getTapTimeout())) {
+ if (!started) {
return false;
}
- if (!started) {
- return false;
+ wasClockwiseRotating = detector.getRotationDegreesDelta() > 0;
+ if (scaleBeginTime != 0) {
+ rotateGestureOccurred = true;
}
+
// rotation constitutes translation of anything except the center of
// rotation, so cancel both location and bearing tracking if required
trackingSettings.resetTrackingModesIfRequired(true, true, false);
- // Get rotate value
- double bearing = transform.getRawBearing();
- bearing += detector.getRotationDegreesDelta();
+ // Calculate map bearing value
+ double bearing = transform.getRawBearing() + angle;
// Rotate the map
if (focalPoint != null) {
@@ -585,6 +743,83 @@ final class MapGestureDetector {
}
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;
+ }
+
+ double angularVelocity = calculateVelocityVector(detector);
+ if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !animating) {
+ animateRotateVelocity();
+ } else if (!animating) {
+ reset();
+ }
+ }
+
+ private void reset() {
+ beginTime = 0;
+ started = false;
+ animating = false;
+ rotateGestureOccurred = false;
+ }
+
+ private void animateRotateVelocity() {
+ animating = true;
+ double currentRotation = transform.getRawBearing();
+ double rotateAdditionDegrees = calculateVelocityInDegrees();
+ createAnimator(currentRotation, rotateAdditionDegrees).start();
+ }
+
+ 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 double calculateVelocityInDegrees() {
+ double angleRadians = Math.atan2(velocityTracker.getXVelocity(), velocityTracker.getYVelocity());
+ double angle = angleRadians / (Math.PI / 180);
+ if (angle <= 0) {
+ angle += 360;
+ }
+
+ // limit the angle
+ angle = angle / ROTATE_LIMITATION_ANGLE;
+
+ // correct direction
+ if (!wasClockwiseRotating) {
+ angle = -angle;
+ }
+
+ return angle;
+ }
+
+ 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() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ reset();
+ }
+ });
+ return animator;
+ }
}
/**
@@ -592,9 +827,8 @@ final class MapGestureDetector {
*/
private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener {
- long beginTime = 0;
- float totalDelta = 0.0f;
- boolean started = false;
+ private long beginTime = 0;
+ private float totalDelta = 0.0f;
@Override
public boolean onShoveBegin(ShoveGestureDetector detector) {
@@ -604,10 +838,6 @@ final class MapGestureDetector {
// notify camera change listener
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- beginTime = detector.getEventTime();
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_PITCH_START, transform));
return true;
}
@@ -615,8 +845,7 @@ final class MapGestureDetector {
public void onShoveEnd(ShoveGestureDetector detector) {
beginTime = 0;
totalDelta = 0.0f;
- started = false;
- dragStarted = false;
+ tiltGestureOccurred = false;
}
@Override
@@ -625,22 +854,26 @@ final class MapGestureDetector {
return false;
}
- // If tilt is large enough ignore a tap
- // Also if zoom already started, don't tilt
- totalDelta += detector.getShovePixelsDelta();
- if (!zoomStarted && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) {
- started = true;
- }
-
// Ignore short touches in case it is a tap
// Also ignore small tilt
long time = detector.getEventTime();
long interval = time - beginTime;
- if (!started && (interval <= ViewConfiguration.getTapTimeout())) {
+ if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
return false;
}
- if (!started) {
+ // 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));
+ }
+
+ if (!tiltGestureOccurred) {
return false;
}
@@ -651,9 +884,6 @@ final class MapGestureDetector {
// Tilt the map
transform.setTilt(pitch);
-
- dragStarted = true;
-
return true;
}
}
@@ -673,4 +903,36 @@ final class MapGestureDetector {
void setOnScrollListener(MapboxMap.OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
+
+ void addOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) {
+ onMapClickListenerList.add(onMapClickListener);
+ }
+
+ void removeOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) {
+ onMapClickListenerList.remove(onMapClickListener);
+ }
+
+ void addOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) {
+ onMapLongClickListenerList.add(onMapLongClickListener);
+ }
+
+ void removeOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) {
+ onMapLongClickListenerList.remove(onMapLongClickListener);
+ }
+
+ void addOnFlingListener(MapboxMap.OnFlingListener onFlingListener) {
+ onFlingListenerList.add(onFlingListener);
+ }
+
+ void removeOnFlingListener(MapboxMap.OnFlingListener onFlingListener) {
+ onFlingListenerList.remove(onFlingListener);
+ }
+
+ void addOnScrollListener(MapboxMap.OnScrollListener onScrollListener) {
+ onScrollListenerList.add(onScrollListener);
+ }
+
+ void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) {
+ onScrollListenerList.remove(onScrollListener);
+ }
}
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 7175242282..d1f01a30f7 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
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.maps;
import android.graphics.PointF;
import android.os.Handler;
+import android.os.Looper;
import android.support.annotation.NonNull;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -204,7 +205,7 @@ final class MapKeyListener {
currentTrackballLongPressTimeOut = null;
}
currentTrackballLongPressTimeOut = new TrackballLongPressTimeOut();
- new Handler().postDelayed(currentTrackballLongPressTimeOut,
+ new Handler(Looper.getMainLooper()).postDelayed(currentTrackballLongPressTimeOut,
ViewConfiguration.getLongPressTimeout());
return true;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index 19cad1d8e0..256f49ef52 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
@@ -1,13 +1,10 @@
package com.mapbox.mapboxsdk.maps;
-import android.app.Activity;
-import android.app.Fragment;
import android.content.Context;
-import android.graphics.Canvas;
import android.graphics.PointF;
-import android.graphics.SurfaceTexture;
+import android.opengl.GLSurfaceView;
+import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.support.annotation.CallSuper;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
@@ -18,33 +15,40 @@ import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ZoomButtonsController;
-
-import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
+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 timber.log.Timber;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION;
+import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE;
/**
* <p>
@@ -62,17 +66,26 @@ import java.util.List;
*/
public class MapView extends FrameLayout {
+ private final MapCallback mapCallback = new MapCallback();
+ private MapboxMap mapboxMap;
+
private NativeMapView nativeMapView;
- private boolean textureMode;
+ private MapboxMapOptions mapboxMapOptions;
private boolean destroyed;
- private boolean hasSurface;
- private MapboxMap mapboxMap;
- private MapCallback mapCallback;
+ private MyLocationView myLocationView;
+ private CompassView compassView;
+ private PointF focalPoint;
+ private ImageView attrView;
+ private ImageView logoView;
private MapGestureDetector mapGestureDetector;
private MapKeyListener mapKeyListener;
private MapZoomButtonController mapZoomButtonController;
+ private Bundle savedInstanceState;
+ private final CopyOnWriteArrayList<OnMapChangedListener> onMapChangedListeners = new CopyOnWriteArrayList<>();
+
+ private MapRenderer mapRenderer;
@UiThread
public MapView(@NonNull Context context) {
@@ -100,28 +113,42 @@ public class MapView extends FrameLayout {
private void initialise(@NonNull final Context context, @NonNull final MapboxMapOptions options) {
if (isInEditMode()) {
- // in IDE, show preview map
- LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_preview, this);
+ // in IDE layout editor, just return
return;
}
-
- // determine render surface
- textureMode = options.getTextureMode();
+ mapboxMapOptions = options;
// inflate view
View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this);
- CompassView compassView = (CompassView) view.findViewById(R.id.compassView);
- MyLocationView myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView);
- ImageView attrView = (ImageView) view.findViewById(R.id.attributionView);
+ 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);
// add accessibility support
setContentDescription(context.getString(R.string.mapbox_mapActionDescription));
+ setWillNotDraw(false);
- // create native Map object
- nativeMapView = new NativeMapView(this);
+ getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ } else {
+ getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ }
+ initialiseDrawingSurface(options);
+ }
+ });
+ }
+
+ private void initialiseMap() {
+ Context context = getContext();
+ addOnMapChangedListener(mapCallback);
// callback for focal point invalidation
- FocalPointInvalidator focalPoint = new FocalPointInvalidator(compassView);
+ final FocalPointInvalidator focalPointInvalidator = new FocalPointInvalidator();
+ focalPointInvalidator.addListener(createFocalPointChangeListener());
// callback for registering touch listeners
RegisterTouchListener registerTouchListener = new RegisterTouchListener();
@@ -130,13 +157,15 @@ public class MapView extends FrameLayout {
CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator();
// callback for camera change events
- CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher();
+ final CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher();
// setup components for MapboxMap creation
Projection proj = new Projection(nativeMapView);
- UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, view.findViewById(R.id.logoView));
- TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator);
- MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint);
+ 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);
@@ -144,23 +173,32 @@ public class MapView extends FrameLayout {
Markers markers = new MarkerContainer(nativeMapView, this, annotationsArray, iconManager, markerViewManager);
Polygons polygons = new PolygonContainer(nativeMapView, annotationsArray);
Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray);
+ ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(nativeMapView, annotationsArray);
AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray,
- markerViewManager, iconManager, annotations, markers, polygons, polylines);
+ markerViewManager, iconManager, annotations, markers, polygons, polylines, shapeAnnotations);
Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings,
cameraChangeDispatcher);
+
mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj,
registerTouchListener, annotationManager, cameraChangeDispatcher);
+ focalPointInvalidator.addListener(mapboxMap.createFocalPointChangeListener());
+
+ mapCallback.attachMapboxMap(mapboxMap);
// user input
mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings,
annotationManager, cameraChangeDispatcher);
mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
- MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform);
- mapZoomButtonController = new MapZoomButtonController(this, uiSettings, zoomListener);
+ // overlain zoom buttons
+ mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this));
+ MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform,
+ cameraChangeDispatcher, getWidth(), getHeight());
+ mapZoomButtonController.bind(uiSettings, zoomListener);
+ compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher));
+ compassView.setOnClickListener(createCompassClickListener(cameraChangeDispatcher));
// inject widgets with MapboxMap
- compassView.setMapboxMap(mapboxMap);
myLocationView.setMapboxMap(mapboxMap);
attrView.setOnClickListener(new AttributionDialogManager(context, mapboxMap));
@@ -171,14 +209,58 @@ public class MapView extends FrameLayout {
setFocusableInTouchMode(true);
requestDisallowInterceptTouchEvent(true);
- // allow onDraw invocation
- setWillNotDraw(false);
-
// notify Map object about current connectivity state
nativeMapView.setReachability(ConnectivityReceiver.instance(context).isConnected(context));
// initialise MapboxMap
- mapboxMap.initialise(context, options);
+ if (savedInstanceState == null) {
+ mapboxMap.initialise(context, mapboxMapOptions);
+ } else {
+ mapboxMap.onRestoreInstanceState(savedInstanceState);
+ }
+ }
+
+ private FocalPointChangeListener createFocalPointChangeListener() {
+ return new FocalPointChangeListener() {
+ @Override
+ public void onFocalPointChanged(PointF pointF) {
+ focalPoint = pointF;
+ }
+ };
+ }
+
+ private MapboxMap.OnCompassAnimationListener createCompassAnimationListener(final CameraChangeDispatcher
+ cameraChangeDispatcher) {
+ return new MapboxMap.OnCompassAnimationListener() {
+ @Override
+ public void onCompassAnimation() {
+ cameraChangeDispatcher.onCameraMove();
+ }
+
+ @Override
+ public void onCompassAnimationFinished() {
+ compassView.isAnimating(false);
+ cameraChangeDispatcher.onCameraIdle();
+ }
+ };
+ }
+
+ private OnClickListener createCompassClickListener(final CameraChangeDispatcher cameraChangeDispatcher) {
+ return new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mapboxMap != null && compassView != null) {
+ if (focalPoint != null) {
+ mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION);
+ } else {
+ mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION);
+ }
+ cameraChangeDispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION);
+ compassView.isAnimating(true);
+ compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION);
+ }
+ }
+ };
}
//
@@ -187,103 +269,160 @@ public class MapView extends FrameLayout {
/**
* <p>
- * You must call this method from the parent's {@link android.app.Activity#onCreate(Bundle)} or
- * {@link android.app.Fragment#onCreate(Bundle)}.
+ * You must call this method from the parent's Activity#onCreate(Bundle)} or
+ * Fragment#onViewCreated(View, Bundle).
* </p>
- * You must set a valid access token with {@link Mapbox#getInstance(Context, String)}) before you call this method
- * or an exception will be thrown.
+ * 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.
*
* @param savedInstanceState Pass in the parent's savedInstanceState.
- * @see Mapbox#getInstance(Context, String)
+ * @see com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)
*/
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent());
} else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
- mapboxMap.onRestoreInstanceState(savedInstanceState);
+ this.savedInstanceState = savedInstanceState;
}
-
- initialiseDrawingSurface(textureMode);
- addOnMapChangedListener(mapCallback = new MapCallback(mapboxMap));
}
- private void initialiseDrawingSurface(boolean textureMode) {
- nativeMapView.initializeDisplay();
- nativeMapView.initializeContext();
- if (textureMode) {
+ private void initialiseDrawingSurface(MapboxMapOptions options) {
+ if (options.getTextureMode()) {
TextureView textureView = new TextureView(getContext());
- textureView.setSurfaceTextureListener(new SurfaceTextureListener());
+ mapRenderer = new TextureViewMapRenderer(getContext(), textureView, options.getLocalIdeographFontFamily()) {
+ @Override
+ protected void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ initRenderSurface();
+ super.onSurfaceCreated(gl, config);
+ }
+ };
+
addView(textureView, 0);
} else {
- SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
- surfaceView.getHolder().addCallback(new SurfaceCallback());
- surfaceView.setVisibility(View.VISIBLE);
+ GLSurfaceView glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView);
+ glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
+ mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView, options.getLocalIdeographFontFamily()) {
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ initRenderSurface();
+ super.onSurfaceCreated(gl, config);
+ }
+ };
+
+ glSurfaceView.setVisibility(View.VISIBLE);
}
+
+ nativeMapView = new NativeMapView(this, mapRenderer);
+ nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ private void initRenderSurface() {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ // Initialise only when not destroyed and only once
+ if (!destroyed && mapboxMap == null) {
+ initialiseMap();
+ mapboxMap.onStart();
+ }
+ }
+ });
}
/**
- * You must call this method from the parent's {@link android.app.Activity#onSaveInstanceState(Bundle)}
- * or {@link android.app.Fragment#onSaveInstanceState(Bundle)}.
+ * You must call this method from the parent's Activity#onSaveInstanceState(Bundle)
+ * or Fragment#onSaveInstanceState(Bundle).
*
* @param outState Pass in the parent's outState.
*/
-
@UiThread
public void onSaveInstanceState(@NonNull Bundle outState) {
- outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
- mapboxMap.onSaveInstanceState(outState);
+ if (mapboxMap != null) {
+ outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
+ mapboxMap.onSaveInstanceState(outState);
+ }
}
/**
- * You must call this method from the parent's {@link Activity#onStart()} or {@link Fragment#onStart()}
+ * You must call this method from the parent's Activity#onStart() or Fragment#onStart()
*/
@UiThread
public void onStart() {
- mapboxMap.onStart();
ConnectivityReceiver.instance(getContext()).activate();
+ FileSource.getInstance(getContext()).activate();
+ if (mapboxMap != null) {
+ mapboxMap.onStart();
+ }
+
+ if (mapRenderer != null) {
+ mapRenderer.onStart();
+ }
}
/**
- * You must call this method from the parent's {@link Activity#onResume()} or {@link Fragment#onResume()}.
+ * You must call this method from the parent's Activity#onResume() or Fragment#onResume().
*/
@UiThread
public void onResume() {
- // replaced by onStart in v5.0.0
+ if (mapRenderer != null) {
+ mapRenderer.onResume();
+ }
}
/**
- * You must call this method from the parent's {@link Activity#onPause()} or {@link Fragment#onPause()}.
+ * You must call this method from the parent's Activity#onPause() or Fragment#onPause().
*/
@UiThread
public void onPause() {
- // replaced by onStop in v5.0.0
+ if (mapRenderer != null) {
+ mapRenderer.onPause();
+ }
}
/**
- * You must call this method from the parent's {@link Activity#onStop()} or {@link Fragment#onStop()}.
+ * You must call this method from the parent's Activity#onStop() or Fragment#onStop().
*/
@UiThread
public void onStop() {
- mapboxMap.onStop();
+ if (mapboxMap != null) {
+ // map was destroyed before it was started
+ mapboxMap.onStop();
+ }
+
+ if (mapRenderer != null) {
+ mapRenderer.onStop();
+ }
+
ConnectivityReceiver.instance(getContext()).deactivate();
+ FileSource.getInstance(getContext()).deactivate();
}
/**
- * You must call this method from the parent's {@link Activity#onDestroy()} or {@link Fragment#onDestroy()}.
+ * You must call this method from the parent's Activity#onDestroy() or Fragment#onDestroyView().
*/
@UiThread
public void onDestroy() {
destroyed = true;
- nativeMapView.terminateContext();
- nativeMapView.terminateDisplay();
- nativeMapView.destroySurface();
- nativeMapView.destroy();
- nativeMapView = null;
+ mapCallback.clearOnMapReadyCallbacks();
+
+ if (nativeMapView != null) {
+ // null when destroying an activity programmatically mapbox-navigation-android/issues/503
+ nativeMapView.destroy();
+ nativeMapView = null;
+ }
+
+ if (mapRenderer != null) {
+ mapRenderer.onDestroy();
+ }
}
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (!isMapInitialized() || !isZoomButtonControllerInitialized()) {
+ return super.onTouchEvent(event);
+ }
+
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mapZoomButtonController.setVisible(true);
}
@@ -312,11 +451,18 @@ public class MapView extends FrameLayout {
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
+ if (mapGestureDetector == null) {
+ return super.onGenericMotionEvent(event);
+ }
return mapGestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
}
@Override
public boolean onHoverEvent(MotionEvent event) {
+ if (!isZoomButtonControllerInitialized()) {
+ return super.onHoverEvent(event);
+ }
+
switch (event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
@@ -334,28 +480,13 @@ public class MapView extends FrameLayout {
}
/**
- * You must call this method from the parent's {@link Activity#onLowMemory()} or {@link Fragment#onLowMemory()}.
+ * You must call this method from the parent's Activity#onLowMemory() or Fragment#onLowMemory().
*/
@UiThread
public void onLowMemory() {
nativeMapView.onLowMemory();
}
- // Called when debug mode is enabled to update a FPS counter
- // Called via JNI from NativeMapView
- // Forward to any listener
- protected void onFpsChanged(final double fps) {
- final MapboxMap.OnFpsChangedListener listener = mapboxMap.getOnFpsChangedListener();
- if (listener != null) {
- post(new Runnable() {
- @Override
- public void run() {
- listener.onFpsChanged(fps);
- }
- });
- }
- }
-
/**
* <p>
* Loads a new map style from the specified URL.
@@ -388,7 +519,10 @@ public class MapView extends FrameLayout {
if (destroyed) {
return;
}
-
+ if (!isMapInitialized()) {
+ mapboxMapOptions.styleUrl(url);
+ return;
+ }
nativeMapView.setStyleUrl(url);
}
@@ -396,119 +530,17 @@ public class MapView extends FrameLayout {
// Rendering
//
- // Called when the map needs to be rerendered
- // Called via JNI from NativeMapView
- protected void onInvalidate() {
- postInvalidate();
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- if (isInEditMode()) {
- return;
- }
-
- if (destroyed) {
- return;
- }
-
- if (!hasSurface) {
- return;
- }
-
- nativeMapView.render();
- }
-
@Override
protected void onSizeChanged(int width, int height, int oldw, int oldh) {
if (destroyed) {
return;
}
- if (!isInEditMode()) {
+ if (!isInEditMode() && isMapInitialized()) {
nativeMapView.resizeView(width, height);
}
}
- private class SurfaceCallback implements SurfaceHolder.Callback {
-
- private Surface surface;
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- nativeMapView.createSurface(surface = holder.getSurface());
- hasSurface = true;
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- if (destroyed) {
- return;
- }
- nativeMapView.resizeFramebuffer(width, height);
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- hasSurface = false;
-
- if (nativeMapView != null) {
- nativeMapView.destroySurface();
- }
- surface.release();
- }
- }
-
- // This class handles TextureView callbacks
- private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
-
- private Surface surface;
-
- // Called when the native surface texture has been created
- // Must do all EGL/GL ES initialization here
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- nativeMapView.createSurface(this.surface = new Surface(surface));
- nativeMapView.resizeFramebuffer(width, height);
- hasSurface = true;
- }
-
- // Called when the native surface texture has been destroyed
- // Must do all EGL/GL ES destruction here
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- hasSurface = false;
-
- if (nativeMapView != null) {
- nativeMapView.destroySurface();
- }
- this.surface.release();
- return true;
- }
-
- // Called when the format or size of the native surface texture has been changed
- // Must handle window resizing here.
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- if (destroyed) {
- return;
- }
-
- nativeMapView.resizeFramebuffer(width, height);
- }
-
- // Called when the SurfaceTexure frame is drawn to screen
- // Must sync with UI here
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- if (destroyed) {
- return;
- }
- mapboxMap.onUpdateRegionChange();
- }
- }
-
//
// View events
//
@@ -518,7 +550,7 @@ public class MapView extends FrameLayout {
@CallSuper
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- if (mapZoomButtonController != null) {
+ if (isZoomButtonControllerInitialized()) {
mapZoomButtonController.setVisible(false);
}
}
@@ -526,16 +558,29 @@ public class MapView extends FrameLayout {
// Called when view is hidden and shown
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
- if (isInEditMode() || mapZoomButtonController == null) {
+ if (isInEditMode()) {
return;
}
- mapZoomButtonController.setVisible(visibility == View.VISIBLE);
+
+ if (isZoomButtonControllerInitialized()) {
+ mapZoomButtonController.setVisible(visibility == View.VISIBLE);
+ }
}
//
// Map events
//
+ void onMapChange(int rawChange) {
+ for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) {
+ try {
+ onMapChangedListener.onMapChanged(rawChange);
+ } catch (RuntimeException err) {
+ Timber.e(err, "Exception in MapView.OnMapChangedListener");
+ }
+ }
+ }
+
/**
* <p>
* Add a callback that's invoked when the displayed map view changes.
@@ -547,7 +592,7 @@ public class MapView extends FrameLayout {
*/
public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
- nativeMapView.addOnMapChangedListener(listener);
+ onMapChangedListeners.add(listener);
}
}
@@ -559,7 +604,7 @@ public class MapView extends FrameLayout {
*/
public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) {
if (listener != null) {
- nativeMapView.removeOnMapChangedListener(listener);
+ onMapChangedListeners.remove(listener);
}
}
@@ -579,6 +624,14 @@ public class MapView extends FrameLayout {
}
}
+ private boolean isMapInitialized() {
+ return nativeMapView != null;
+ }
+
+ private boolean isZoomButtonControllerInitialized() {
+ return mapZoomButtonController != null;
+ }
+
MapboxMap getMapboxMap() {
return mapboxMap;
}
@@ -783,7 +836,7 @@ public class MapView extends FrameLayout {
public static final int DID_FINISH_LOADING_STYLE = 14;
/**
- * This {@link MapChange} is triggered when a source attribution changes.
+ * This {@link MapChange} is triggered when a source changes.
* <p>
* Register to {@link MapChange} events with {@link MapView#addOnMapChangedListener(OnMapChangedListener)}.
* </p>
@@ -826,10 +879,10 @@ public class MapView extends FrameLayout {
private class FocalPointInvalidator implements FocalPointChangeListener {
- private final FocalPointChangeListener[] focalPointChangeListeners;
+ private final List<FocalPointChangeListener> focalPointChangeListeners = new ArrayList<>();
- FocalPointInvalidator(FocalPointChangeListener... listeners) {
- focalPointChangeListeners = listeners;
+ void addListener(FocalPointChangeListener focalPointChangeListener) {
+ focalPointChangeListeners.add(focalPointChangeListener);
}
@Override
@@ -844,36 +897,83 @@ public class MapView extends FrameLayout {
private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener {
@Override
- public void onRegisterMapClickListener(MapboxMap.OnMapClickListener listener) {
+ public void onSetMapClickListener(MapboxMap.OnMapClickListener listener) {
mapGestureDetector.setOnMapClickListener(listener);
}
@Override
- public void onRegisterMapLongClickListener(MapboxMap.OnMapLongClickListener listener) {
+ public void onAddMapClickListener(MapboxMap.OnMapClickListener listener) {
+ mapGestureDetector.addOnMapClickListener(listener);
+ }
+
+ @Override
+ public void onRemoveMapClickListener(MapboxMap.OnMapClickListener listener) {
+ mapGestureDetector.removeOnMapClickListener(listener);
+ }
+
+ @Override
+ public void onSetMapLongClickListener(MapboxMap.OnMapLongClickListener listener) {
mapGestureDetector.setOnMapLongClickListener(listener);
}
@Override
- public void onRegisterScrollListener(MapboxMap.OnScrollListener listener) {
+ public void onAddMapLongClickListener(MapboxMap.OnMapLongClickListener listener) {
+ mapGestureDetector.addOnMapLongClickListener(listener);
+ }
+
+ @Override
+ public void onRemoveMapLongClickListener(MapboxMap.OnMapLongClickListener listener) {
+ mapGestureDetector.removeOnMapLongClickListener(listener);
+ }
+
+ @Override
+ public void onSetScrollListener(MapboxMap.OnScrollListener listener) {
mapGestureDetector.setOnScrollListener(listener);
}
@Override
- public void onRegisterFlingListener(MapboxMap.OnFlingListener listener) {
+ public void onAddScrollListener(MapboxMap.OnScrollListener listener) {
+ mapGestureDetector.addOnScrollListener(listener);
+ }
+
+ @Override
+ public void onRemoveScrollListener(MapboxMap.OnScrollListener listener) {
+ mapGestureDetector.removeOnScrollListener(listener);
+ }
+
+ @Override
+ public void onSetFlingListener(MapboxMap.OnFlingListener listener) {
mapGestureDetector.setOnFlingListener(listener);
}
+
+ @Override
+ public void onAddFlingListener(MapboxMap.OnFlingListener listener) {
+ mapGestureDetector.addOnFlingListener(listener);
+ }
+
+ @Override
+ public void onRemoveFlingListener(MapboxMap.OnFlingListener listener) {
+ mapGestureDetector.removeOnFlingListener(listener);
+ }
}
- private class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener {
+ 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) {
+ MapZoomControllerListener(MapGestureDetector detector, UiSettings uiSettings, Transform transform,
+ CameraChangeDispatcher dispatcher, float mapWidth, float mapHeight) {
this.mapGestureDetector = detector;
this.uiSettings = uiSettings;
this.transform = transform;
+ this.cameraChangeDispatcher = dispatcher;
+ this.mapWidth = mapWidth;
+ this.mapHeight = mapHeight;
}
// Not used
@@ -886,6 +986,7 @@ public class MapView extends FrameLayout {
@Override
public void onZoom(boolean zoomIn) {
if (uiSettings.isZoomGesturesEnabled()) {
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_ANIMATION);
onZoom(zoomIn, mapGestureDetector.getFocalPoint());
}
}
@@ -894,7 +995,7 @@ public class MapView extends FrameLayout {
if (focalPoint != null) {
transform.zoom(zoomIn, focalPoint);
} else {
- PointF centerPoint = new PointF(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
+ PointF centerPoint = new PointF(mapWidth / 2, mapHeight / 2);
transform.zoom(zoomIn, centerPoint);
}
}
@@ -923,11 +1024,11 @@ public class MapView extends FrameLayout {
private static class MapCallback implements OnMapChangedListener {
- private final MapboxMap mapboxMap;
+ private MapboxMap mapboxMap;
private final List<OnMapReadyCallback> onMapReadyCallbackList = new ArrayList<>();
private boolean initialLoad = true;
- MapCallback(MapboxMap mapboxMap) {
+ void attachMapboxMap(MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
}
@@ -935,14 +1036,9 @@ public class MapView extends FrameLayout {
public void onMapChanged(@MapChange int change) {
if (change == DID_FINISH_LOADING_STYLE && initialLoad) {
initialLoad = false;
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- mapboxMap.onPreMapReady();
- onMapReady();
- mapboxMap.onPostMapReady();
- }
- });
+ mapboxMap.onPreMapReady();
+ onMapReady();
+ mapboxMap.onPostMapReady();
} else if (change == DID_FINISH_RENDERING_FRAME || change == DID_FINISH_RENDERING_FRAME_FULLY_RENDERED) {
mapboxMap.onUpdateFullyRendered();
} else if (change == REGION_IS_CHANGING || change == REGION_DID_CHANGE || change == DID_FINISH_LOADING_MAP) {
@@ -969,5 +1065,9 @@ public class MapView extends FrameLayout {
void addOnMapReadyCallback(OnMapReadyCallback callback) {
onMapReadyCallbackList.add(callback);
}
+
+ void clearOnMapReadyCallbacks() {
+ onMapReadyCallbackList.clear();
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java
index 16513904c5..018c8eb5bb 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java
@@ -1,7 +1,6 @@
package com.mapbox.mapboxsdk.maps;
import android.support.annotation.NonNull;
-import android.view.View;
import android.widget.ZoomButtonsController;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
@@ -12,21 +11,25 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants;
* Allows single touch only devices to zoom in and out.
* </p>
*/
-final class MapZoomButtonController extends ZoomButtonsController {
+final class MapZoomButtonController {
private UiSettings uiSettings;
+ private ZoomButtonsController zoomButtonsController;
- MapZoomButtonController(@NonNull View ownerView, @NonNull UiSettings uiSettings, @NonNull OnZoomListener listener) {
- super(ownerView);
+ MapZoomButtonController(@NonNull ZoomButtonsController zoomButtonsController) {
+ this.zoomButtonsController = zoomButtonsController;
+ this.zoomButtonsController.setZoomSpeed(MapboxConstants.ANIMATION_DURATION);
+ }
+
+ void bind(UiSettings uiSettings, ZoomButtonsController.OnZoomListener onZoomListener) {
this.uiSettings = uiSettings;
- setZoomSpeed(MapboxConstants.ANIMATION_DURATION);
- setOnZoomListener(listener);
+ zoomButtonsController.setOnZoomListener(onZoomListener);
}
- @Override
- public void setVisible(boolean visible) {
- if (uiSettings.isZoomControlsEnabled()) {
- super.setVisible(visible);
+ void setVisible(boolean visible) {
+ if (uiSettings != null && !uiSettings.isZoomControlsEnabled()) {
+ return;
}
+ zoomButtonsController.setVisible(visible);
}
}
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 f60ddf616a..bf98e6bbc1 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
@@ -37,7 +37,6 @@ 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.location.LocationSource;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.layers.Layer;
@@ -45,8 +44,10 @@ 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;
import java.util.List;
import timber.log.Timber;
@@ -60,6 +61,7 @@ import timber.log.Timber;
* Note: Similar to a View object, a MapboxMap should only be read and modified from the main thread.
* </p>
*/
+@UiThread
public final class MapboxMap {
private final NativeMapView nativeMapView;
@@ -75,6 +77,7 @@ public final class MapboxMap {
private final OnRegisterTouchListener onRegisterTouchListener;
private MapboxMap.OnFpsChangedListener onFpsChangedListener;
+ private PointF focalPoint;
MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking,
MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener,
@@ -100,6 +103,7 @@ public final class MapboxMap {
setDebugActive(options.getDebugActive());
setApiBaseUrl(options);
setStyleUrl(options);
+ setPrefetchesTiles(options);
}
/**
@@ -132,6 +136,7 @@ public final class MapboxMap {
outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl());
trackingSettings.onSaveInstanceState(outState);
uiSettings.onSaveInstanceState(outState);
+ myLocationViewSettings.onSaveInstanceState(outState);
}
/**
@@ -141,12 +146,17 @@ public final class MapboxMap {
*/
void onRestoreInstanceState(Bundle savedInstanceState) {
final CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION);
- if (cameraPosition != null) {
- moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder(cameraPosition).build()));
- }
+ myLocationViewSettings.onRestoreInstanceState(savedInstanceState);
uiSettings.onRestoreInstanceState(savedInstanceState);
trackingSettings.onRestoreInstanceState(savedInstanceState);
+
+ if (cameraPosition != null) {
+ moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder(cameraPosition).build())
+ );
+ }
+
nativeMapView.setDebug(savedInstanceState.getBoolean(MapboxConstants.STATE_DEBUG_ACTIVE));
final String styleUrl = savedInstanceState.getString(MapboxConstants.STATE_STYLE_URL);
@@ -202,7 +212,6 @@ public final class MapboxMap {
*
* @return Duration in milliseconds
*/
- @UiThread
public long getTransitionDuration() {
return nativeMapView.getTransitionDuration();
}
@@ -212,7 +221,6 @@ public final class MapboxMap {
*
* @param durationMs Duration in milliseconds
*/
- @UiThread
public void setTransitionDuration(long durationMs) {
nativeMapView.setTransitionDuration(durationMs);
}
@@ -225,7 +233,6 @@ public final class MapboxMap {
*
* @return Delay in milliseconds
*/
- @UiThread
public long getTransitionDelay() {
return nativeMapView.getTransitionDelay();
}
@@ -235,17 +242,44 @@ public final class MapboxMap {
*
* @param delayMs Delay in milliseconds
*/
- @UiThread
public void setTransitionDelay(long delayMs) {
nativeMapView.setTransitionDelay(delayMs);
}
/**
+ * Sets tile pre-fetching from MapboxOptions.
+ *
+ * @param options the options object
+ */
+ private void setPrefetchesTiles(@NonNull MapboxMapOptions options) {
+ setPrefetchesTiles(options.getPrefetchesTiles());
+ }
+
+ /**
+ * Enable or disable tile pre-fetching. Pre-fetching makes sure that a low-resolution
+ * tile is rendered as soon as possible at the expense of a little bandwidth.
+ *
+ * @param enable true to enable
+ */
+ public void setPrefetchesTiles(boolean enable) {
+ nativeMapView.setPrefetchesTiles(enable);
+ }
+
+ /**
+ * Check whether tile pre-fetching is enabled or not.
+ *
+ * @return true if enabled
+ * @see MapboxMap#setPrefetchesTiles(boolean)
+ */
+ public boolean getPrefetchesTiles() {
+ return nativeMapView.getPrefetchesTiles();
+ }
+
+ /**
* Retrieve all the layers in the style
*
* @return all the layers in the current style
*/
- @UiThread
public List<Layer> getLayers() {
return nativeMapView.getLayers();
}
@@ -257,7 +291,6 @@ public final class MapboxMap {
* @return the layer, if present in the style
*/
@Nullable
- @UiThread
public Layer getLayer(@NonNull String layerId) {
return nativeMapView.getLayer(layerId);
}
@@ -270,13 +303,12 @@ public final class MapboxMap {
* @return the casted Layer, null if another type
*/
@Nullable
- @UiThread
public <T extends Layer> T getLayerAs(@NonNull String layerId) {
try {
// noinspection unchecked
return (T) nativeMapView.getLayer(layerId);
} catch (ClassCastException exception) {
- Timber.e(String.format("Layer: %s is a different type: %s", layerId, exception));
+ Timber.e(exception, "Layer: %s is a different type: ", layerId);
return null;
}
}
@@ -286,7 +318,6 @@ public final class MapboxMap {
*
* @param layer the layer to add
*/
- @UiThread
public void addLayer(@NonNull Layer layer) {
nativeMapView.addLayer(layer);
}
@@ -297,7 +328,6 @@ public final class MapboxMap {
* @param layer the layer to add
* @param below the layer id to add this layer before
*/
- @UiThread
public void addLayerBelow(@NonNull Layer layer, @NonNull String below) {
nativeMapView.addLayerBelow(layer, below);
}
@@ -308,7 +338,6 @@ public final class MapboxMap {
* @param layer the layer to add
* @param above the layer id to add this layer above
*/
- @UiThread
public void addLayerAbove(@NonNull Layer layer, @NonNull String above) {
nativeMapView.addLayerAbove(layer, above);
}
@@ -320,7 +349,6 @@ public final class MapboxMap {
* @param layer the layer to add
* @param index the index to insert the layer at
*/
- @UiThread
public void addLayerAt(@NonNull Layer layer, @IntRange(from = 0) int index) {
nativeMapView.addLayerAt(layer, index);
}
@@ -331,7 +359,6 @@ public final class MapboxMap {
* @param layerId the layer to remove
* @return the removed layer or null if not found
*/
- @UiThread
@Nullable
public Layer removeLayer(@NonNull String layerId) {
return nativeMapView.removeLayer(layerId);
@@ -343,7 +370,6 @@ public final class MapboxMap {
* @param layer the layer to remove
* @return the layer
*/
- @UiThread
@Nullable
public Layer removeLayer(@NonNull Layer layer) {
return nativeMapView.removeLayer(layer);
@@ -355,7 +381,6 @@ public final class MapboxMap {
* @param index the layer index
* @return the removed layer or null if not found
*/
- @UiThread
@Nullable
public Layer removeLayerAt(@IntRange(from = 0) int index) {
return nativeMapView.removeLayerAt(index);
@@ -366,7 +391,6 @@ public final class MapboxMap {
*
* @return all the sources in the current style
*/
- @UiThread
public List<Source> getSources() {
return nativeMapView.getSources();
}
@@ -378,7 +402,6 @@ public final class MapboxMap {
* @return the source if present in the current style
*/
@Nullable
- @UiThread
public Source getSource(@NonNull String sourceId) {
return nativeMapView.getSource(sourceId);
}
@@ -391,13 +414,12 @@ public final class MapboxMap {
* @return the casted Source, null if another type
*/
@Nullable
- @UiThread
public <T extends Source> T getSourceAs(@NonNull String sourceId) {
try {
// noinspection unchecked
return (T) nativeMapView.getSource(sourceId);
} catch (ClassCastException exception) {
- Timber.e(String.format("Source: %s is a different type: %s", sourceId, exception));
+ Timber.e(exception, "Source: %s is a different type: ", sourceId);
return null;
}
}
@@ -407,7 +429,6 @@ public final class MapboxMap {
*
* @param source the source to add
*/
- @UiThread
public void addSource(@NonNull Source source) {
nativeMapView.addSource(source);
}
@@ -418,7 +439,6 @@ public final class MapboxMap {
* @param sourceId the source to remove
* @return the source handle or null if the source was not present
*/
- @UiThread
@Nullable
public Source removeSource(@NonNull String sourceId) {
return nativeMapView.removeSource(sourceId);
@@ -430,7 +450,6 @@ public final class MapboxMap {
* @param source the source to remove
* @return the source
*/
- @UiThread
@Nullable
public Source removeSource(@NonNull Source source) {
return nativeMapView.removeSource(source);
@@ -442,21 +461,30 @@ public final class MapboxMap {
* @param name the name of the image
* @param image the pre-multiplied Bitmap
*/
- @UiThread
public void addImage(@NonNull String name, @NonNull Bitmap image) {
nativeMapView.addImage(name, image);
}
/**
+ * Adds an images to be used in the map's style
+ */
+ public void addImages(@NonNull HashMap<String, Bitmap> images) {
+ nativeMapView.addImages(images);
+ }
+
+ /**
* Removes an image from the map's style
*
* @param name the name of the image to remove
*/
- @UiThread
public void removeImage(String name) {
nativeMapView.removeImage(name);
}
+ public Bitmap getImage(@NonNull String name) {
+ return nativeMapView.getImage(name);
+ }
+
//
// MinZoom
//
@@ -468,7 +496,6 @@ public final class MapboxMap {
*
* @param minZoom The new minimum zoom level.
*/
- @UiThread
public void setMinZoomPreference(
@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) {
transform.setMinZoom(minZoom);
@@ -476,12 +503,11 @@ public final class MapboxMap {
/**
* <p>
- * Gets the maximum zoom level the map can be displayed at.
+ * Gets the minimum zoom level the map can be displayed at.
* </p>
*
* @return The minimum zoom level.
*/
- @UiThread
public double getMinZoomLevel() {
return transform.getMinZoom();
}
@@ -494,10 +520,12 @@ public final class MapboxMap {
* <p>
* Sets the maximum zoom level the map can be displayed at.
* </p>
+ * <p>
+ * The default maximum zoomn level is 22. The upper bound for this value is 25.5.
+ * </p>
*
* @param maxZoom The new maximum zoom level.
*/
- @UiThread
public void setMaxZoomPreference(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM,
to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) {
transform.setMaxZoom(maxZoom);
@@ -510,7 +538,6 @@ public final class MapboxMap {
*
* @return The maximum zoom level.
*/
- @UiThread
public double getMaxZoomLevel() {
return transform.getMaxZoom();
}
@@ -536,7 +563,10 @@ public final class MapboxMap {
* 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/plugin-locationlayer instead.
*/
+ @Deprecated
public TrackingSettings getTrackingSettings() {
return trackingSettings;
}
@@ -549,7 +579,10 @@ public final class MapboxMap {
* 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/plugin-locationlayer instead.
*/
+ @Deprecated
public MyLocationViewSettings getMyLocationViewSettings() {
return myLocationViewSettings;
}
@@ -587,6 +620,47 @@ 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.
@@ -625,7 +699,6 @@ public final class MapboxMap {
*
* @param update The change that should be applied to the camera.
*/
- @UiThread
public final void moveCamera(CameraUpdate update) {
moveCamera(update, null);
}
@@ -638,7 +711,6 @@ public final class MapboxMap {
* @param update The change that should be applied to the camera
* @param callback the callback to be invoked when an animation finishes or is canceled
*/
- @UiThread
public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) {
new Handler().post(new Runnable() {
@Override
@@ -647,6 +719,10 @@ public final class MapboxMap {
// MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo`
// invalidate camera position to provide OnCameraChange event.
invalidateCameraPosition();
+
+ if (callback != null) {
+ callback.onFinish();
+ }
}
});
}
@@ -659,7 +735,6 @@ public final class MapboxMap {
* @param update The change that should be applied to the camera.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void easeCamera(CameraUpdate update) {
easeCamera(update, MapboxConstants.ANIMATION_DURATION);
}
@@ -674,7 +749,6 @@ public final class MapboxMap {
* positive, otherwise an IllegalArgumentException will be thrown.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void easeCamera(CameraUpdate update, int durationMs) {
easeCamera(update, durationMs, null);
}
@@ -698,7 +772,6 @@ public final class MapboxMap {
* Do not update or ease the camera from within onCancel().
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
easeCamera(update, durationMs, true, callback);
}
@@ -717,7 +790,6 @@ public final class MapboxMap {
* positive, otherwise an IllegalArgumentException will be thrown.
* @param easingInterpolator True for easing interpolator, false for linear.
*/
- @UiThread
public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator) {
easeCamera(update, durationMs, easingInterpolator, null);
}
@@ -729,8 +801,8 @@ public final class MapboxMap {
* 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 TrackingSettings#setDismissTrackingModeForCameraPositionChange(boolean)} with false before invoking this
- * method and calling it with true in the {@link CancelableCallback#onFinish()}.
+ * {@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.
@@ -743,7 +815,6 @@ public final class MapboxMap {
* by a later camera movement or a user gesture, onCancel() will be called.
* Do not update or ease the camera from within onCancel().
*/
- @UiThread
public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator,
final MapboxMap.CancelableCallback callback) {
easeCamera(update, durationMs, easingInterpolator, callback, false);
@@ -756,8 +827,8 @@ public final class MapboxMap {
* 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 TrackingSettings#setDismissTrackingModeForCameraPositionChange(boolean)} with false before invoking this
- * method and calling it with true in the {@link CancelableCallback#onFinish()}.
+ * {@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.
@@ -771,9 +842,12 @@ public final class MapboxMap {
* Do not update or ease the camera from within onCancel().
* @param isDismissable true will allow animated camera changes dismiss a tracking mode.
*/
- @UiThread
public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator,
final MapboxMap.CancelableCallback callback, final boolean isDismissable) {
+
+ if (durationMs <= 0) {
+ throw new IllegalArgumentException("Null duration passed into easeCamera");
+ }
new Handler().post(new Runnable() {
@Override
public void run() {
@@ -791,7 +865,6 @@ public final class MapboxMap {
* @param update The change that should be applied to the camera.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void animateCamera(CameraUpdate update) {
animateCamera(update, MapboxConstants.ANIMATION_DURATION, null);
}
@@ -808,7 +881,6 @@ public final class MapboxMap {
* called. Do not update or animate the camera from within onCancel().
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) {
animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback);
}
@@ -824,7 +896,6 @@ public final class MapboxMap {
* positive, otherwise an IllegalArgumentException will be thrown.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void animateCamera(CameraUpdate update, int durationMs) {
animateCamera(update, durationMs, null);
}
@@ -847,9 +918,11 @@ public final class MapboxMap {
* isn't required, leave it as null.
* @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates.
*/
- @UiThread
public final void animateCamera(final CameraUpdate update, final int durationMs,
final MapboxMap.CancelableCallback callback) {
+ if (durationMs <= 0) {
+ throw new IllegalArgumentException("Null duration passed into animageCamera");
+ }
new Handler().post(new Runnable() {
@Override
public void run() {
@@ -880,7 +953,12 @@ public final class MapboxMap {
}
/**
- * Set focal bearing.
+ * Transform the map bearing given a bearing, focal point coordinates, and a duration.
+ *
+ * @param bearing The bearing of the Map to be transformed to
+ * @param focalX The x coordinate of the focal point
+ * @param focalY The y coordinate of the focal point
+ * @param duration The duration of the transformation
*/
public void setFocalBearing(double bearing, float focalX, float focalY, long duration) {
transform.setBearing(bearing, focalX, focalY, duration);
@@ -913,7 +991,6 @@ public final class MapboxMap {
*
* @return If true, map debug information is currently shown.
*/
- @UiThread
public boolean isDebugActive() {
return nativeMapView.getDebug();
}
@@ -926,7 +1003,6 @@ public final class MapboxMap {
*
* @param debugActive If true, map debug information is shown.
*/
- @UiThread
public void setDebugActive(boolean debugActive) {
nativeMapView.setDebug(debugActive);
}
@@ -940,7 +1016,6 @@ public final class MapboxMap {
*
* @see #isDebugActive()
*/
- @UiThread
public void cycleDebugOptions() {
nativeMapView.cycleDebugOptions();
}
@@ -990,7 +1065,6 @@ public final class MapboxMap {
* @param url The URL of the map style
* @see Style
*/
- @UiThread
public void setStyleUrl(@NonNull String url) {
setStyleUrl(url, null);
}
@@ -1023,7 +1097,6 @@ public final class MapboxMap {
* @param callback The callback that is invoked when the style has loaded.
* @see Style
*/
- @UiThread
public void setStyleUrl(@NonNull final String url, @Nullable final OnStyleLoadedListener callback) {
if (callback != null) {
nativeMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
@@ -1052,10 +1125,9 @@ public final class MapboxMap {
* An error message will be logged in the Android logcat and {@link MapView#DID_FAIL_LOADING_MAP} event will be
* sent.
*
- * @param style The bundled style. Accepts one of the values from {@link Style}.
+ * @param style The bundled style.
* @see Style
*/
- @UiThread
public void setStyle(@Style.StyleUrl String style) {
setStyleUrl(style);
}
@@ -1068,10 +1140,10 @@ public final class MapboxMap {
* An error message will be logged in the Android logcat and {@link MapView#DID_FAIL_LOADING_MAP} event will be
* sent.
*
- * @param style The bundled style. Accepts one of the values from {@link Style}.
+ * @param style The bundled style.
+ * @param callback The callback to be invoked when the style has finished loading
* @see Style
*/
- @UiThread
public void setStyle(@Style.StyleUrl String style, @Nullable OnStyleLoadedListener callback) {
setStyleUrl(style, callback);
}
@@ -1089,16 +1161,36 @@ public final class MapboxMap {
}
/**
- * Returns the map style currently displayed in the map view.
+ * Returns the map style url currently displayed in the map view.
*
* @return The URL of the map style
*/
- @UiThread
@Nullable
public String getStyleUrl() {
return nativeMapView.getStyleUrl();
}
+ /**
+ * Loads a new map style from a json string.
+ * <p>
+ * If the style fails to load or an invalid style URL is set, the map view will become blank.
+ * An error message will be logged in the Android logcat and {@link MapView#DID_FAIL_LOADING_MAP} event will be
+ * sent.
+ * </p>
+ */
+ public void setStyleJson(@NonNull String styleJson) {
+ nativeMapView.setStyleJson(styleJson);
+ }
+
+ /**
+ * Returns the map style json currently displayed in the map view.
+ *
+ * @return The json of the map style
+ */
+ public String getStyleJson() {
+ return nativeMapView.getStyleJson();
+ }
+
//
// Annotations
//
@@ -1113,7 +1205,6 @@ public final class MapboxMap {
* @param markerOptions A marker options object that defines how to render the marker
* @return The {@code Marker} that was added to the map
*/
- @UiThread
@NonNull
public Marker addMarker(@NonNull MarkerOptions markerOptions) {
return annotationManager.addMarker(markerOptions, this);
@@ -1129,7 +1220,6 @@ public final class MapboxMap {
* @param markerOptions A marker options object that defines how to render the marker
* @return The {@code Marker} that was added to the map
*/
- @UiThread
@NonNull
public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) {
return annotationManager.addMarker(markerOptions, this);
@@ -1144,9 +1234,11 @@ public final class MapboxMap {
*
* @param markerOptions A marker options object that defines how to render the marker
* @return The {@code Marker} that was added to the map
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
- @UiThread
@NonNull
+ @Deprecated
public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) {
return annotationManager.addMarker(markerOptions, this, null);
}
@@ -1161,8 +1253,10 @@ public final class MapboxMap {
* @param markerOptions A marker options object that defines how to render the marker
* @param onMarkerViewAddedListener Callback invoked when the View has been added to the map
* @return The {@code Marker} that was added to the map
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
- @UiThread
+ @Deprecated
@NonNull
public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions,
final MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) {
@@ -1178,9 +1272,11 @@ public final class MapboxMap {
*
* @param markerViewOptions A list of markerView options objects that defines how to render the markers
* @return A list of the {@code MarkerView}s that were added to the map
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
- @UiThread
@NonNull
+ @Deprecated
public List<MarkerView> addMarkerViews(@NonNull List<? extends
BaseMarkerViewOptions> markerViewOptions) {
return annotationManager.addMarkerViews(markerViewOptions, this);
@@ -1191,9 +1287,11 @@ public final class MapboxMap {
*
* @param rect the rectangular area on the map to query for markerViews
* @return A list of the markerViews that were found in the rectangle
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
- @UiThread
@NonNull
+ @Deprecated
public List<MarkerView> getMarkerViewsInRect(@NonNull RectF rect) {
return annotationManager.getMarkerViewsInRect(rect);
}
@@ -1208,7 +1306,6 @@ public final class MapboxMap {
* @param markerOptionsList A list of marker options objects that defines how to render the markers
* @return A list of the {@code Marker}s that were added to the map
*/
- @UiThread
@NonNull
public List<Marker> addMarkers(@NonNull List<? extends
BaseMarkerOptions> markerOptionsList) {
@@ -1222,7 +1319,6 @@ public final class MapboxMap {
*
* @param updatedMarker An updated marker object
*/
- @UiThread
public void updateMarker(@NonNull Marker updatedMarker) {
annotationManager.updateMarker(updatedMarker, this);
}
@@ -1233,7 +1329,6 @@ public final class MapboxMap {
* @param polylineOptions A polyline options object that defines how to render the polyline
* @return The {@code Polyine} that was added to the map
*/
- @UiThread
@NonNull
public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) {
return annotationManager.addPolyline(polylineOptions, this);
@@ -1245,7 +1340,6 @@ public final class MapboxMap {
* @param polylineOptionsList A list of polyline options objects that defines how to render the polylines.
* @return A list of the {@code Polyline}s that were added to the map.
*/
- @UiThread
@NonNull
public List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList) {
return annotationManager.addPolylines(polylineOptionsList, this);
@@ -1256,7 +1350,6 @@ public final class MapboxMap {
*
* @param polyline An updated polyline object.
*/
- @UiThread
public void updatePolyline(Polyline polyline) {
annotationManager.updatePolyline(polyline);
}
@@ -1267,7 +1360,6 @@ public final class MapboxMap {
* @param polygonOptions A polygon options object that defines how to render the polygon.
* @return The {@code Polygon} that was added to the map.
*/
- @UiThread
@NonNull
public Polygon addPolygon(@NonNull PolygonOptions polygonOptions) {
return annotationManager.addPolygon(polygonOptions, this);
@@ -1279,7 +1371,6 @@ public final class MapboxMap {
* @param polygonOptionsList A list of polygon options objects that defines how to render the polygons
* @return A list of the {@code Polygon}s that were added to the map
*/
- @UiThread
@NonNull
public List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) {
return annotationManager.addPolygons(polygonOptionsList, this);
@@ -1290,7 +1381,6 @@ public final class MapboxMap {
*
* @param polygon An updated polygon object
*/
- @UiThread
public void updatePolygon(Polygon polygon) {
annotationManager.updatePolygon(polygon);
}
@@ -1303,7 +1393,6 @@ public final class MapboxMap {
*
* @param marker Marker to remove
*/
- @UiThread
public void removeMarker(@NonNull Marker marker) {
annotationManager.removeAnnotation(marker);
}
@@ -1316,7 +1405,6 @@ public final class MapboxMap {
*
* @param polyline Polyline to remove
*/
- @UiThread
public void removePolyline(@NonNull Polyline polyline) {
annotationManager.removeAnnotation(polyline);
}
@@ -1329,7 +1417,6 @@ public final class MapboxMap {
*
* @param polygon Polygon to remove
*/
- @UiThread
public void removePolygon(@NonNull Polygon polygon) {
annotationManager.removeAnnotation(polygon);
}
@@ -1339,7 +1426,6 @@ public final class MapboxMap {
*
* @param annotation The annotation object to remove.
*/
- @UiThread
public void removeAnnotation(@NonNull Annotation annotation) {
annotationManager.removeAnnotation(annotation);
}
@@ -1349,7 +1435,6 @@ public final class MapboxMap {
*
* @param id The identifier associated to the annotation to be removed
*/
- @UiThread
public void removeAnnotation(long id) {
annotationManager.removeAnnotation(id);
}
@@ -1359,7 +1444,6 @@ public final class MapboxMap {
*
* @param annotationList A list of annotation objects to remove.
*/
- @UiThread
public void removeAnnotations(@NonNull List<? extends Annotation> annotationList) {
annotationManager.removeAnnotations(annotationList);
}
@@ -1367,7 +1451,6 @@ public final class MapboxMap {
/**
* Removes all annotations from the map.
*/
- @UiThread
public void removeAnnotations() {
annotationManager.removeAnnotations();
}
@@ -1375,7 +1458,6 @@ public final class MapboxMap {
/**
* Removes all markers, polylines, polygons, overlays, etc from the map.
*/
- @UiThread
public void clear() {
annotationManager.removeAnnotations();
}
@@ -1441,12 +1523,31 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on a marker.
* To unset the callback, use null.
*/
- @UiThread
public void setOnMarkerClickListener(@Nullable OnMarkerClickListener listener) {
annotationManager.setOnMarkerClickListener(listener);
}
/**
+ * Sets a callback that's invoked when the user clicks on a polygon.
+ *
+ * @param listener The callback that's invoked when the user clicks on a polygon.
+ * To unset the callback, use null.
+ */
+ public void setOnPolygonClickListener(@Nullable OnPolygonClickListener listener) {
+ annotationManager.setOnPolygonClickListener(listener);
+ }
+
+ /**
+ * Sets a callback that's invoked when the user clicks on a polyline.
+ *
+ * @param listener The callback that's invoked when the user clicks on a polyline.
+ * To unset the callback, use null.
+ */
+ public void setOnPolylineClickListener(@Nullable OnPolylineClickListener listener) {
+ annotationManager.setOnPolylineClickListener(listener);
+ }
+
+ /**
* <p>
* Selects a marker. The selected marker will have it's info window opened.
* Any other open info windows will be closed unless isAllowConcurrentMultipleOpenInfoWindows()
@@ -1456,7 +1557,6 @@ public final class MapboxMap {
*
* @param marker The marker to select.
*/
- @UiThread
public void selectMarker(@NonNull Marker marker) {
if (marker == null) {
Timber.w("marker was null, so just returning");
@@ -1468,7 +1568,6 @@ public final class MapboxMap {
/**
* Deselects any currently selected marker. All markers will have it's info window closed.
*/
- @UiThread
public void deselectMarkers() {
annotationManager.deselectMarkers();
}
@@ -1478,7 +1577,6 @@ public final class MapboxMap {
*
* @param marker the marker to deselect
*/
- @UiThread
public void deselectMarker(@NonNull Marker marker) {
annotationManager.deselectMarker(marker);
}
@@ -1488,7 +1586,6 @@ public final class MapboxMap {
*
* @return The currently selected marker.
*/
- @UiThread
public List<Marker> getSelectedMarkers() {
return annotationManager.getSelectedMarkers();
}
@@ -1516,7 +1613,6 @@ public final class MapboxMap {
* @param infoWindowAdapter The callback to be invoked when an info window will be shown.
* To unset the callback, use null.
*/
- @UiThread
public void setInfoWindowAdapter(@Nullable InfoWindowAdapter infoWindowAdapter) {
annotationManager.getInfoWindowManager().setInfoWindowAdapter(infoWindowAdapter);
}
@@ -1526,7 +1622,6 @@ public final class MapboxMap {
*
* @return The callback to be invoked when an info window will be shown.
*/
- @UiThread
@Nullable
public InfoWindowAdapter getInfoWindowAdapter() {
return annotationManager.getInfoWindowManager().getInfoWindowAdapter();
@@ -1537,7 +1632,6 @@ public final class MapboxMap {
*
* @param allow If true, map allows concurrent multiple infowindows to be shown.
*/
- @UiThread
public void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) {
annotationManager.getInfoWindowManager().setAllowConcurrentMultipleOpenInfoWindows(allow);
}
@@ -1547,7 +1641,6 @@ public final class MapboxMap {
*
* @return If true, map allows concurrent multiple infowindows to be shown.
*/
- @UiThread
public boolean isAllowConcurrentMultipleOpenInfoWindows() {
return annotationManager.getInfoWindowManager().isAllowConcurrentMultipleOpenInfoWindows();
}
@@ -1569,9 +1662,11 @@ public final class MapboxMap {
}
/**
- * Gets a camera position that would fit a bounds.
+ * Get a camera position that fits a provided bounds and padding.
*
* @param latLngBounds the bounds to constrain the map with
+ * @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
@@ -1589,6 +1684,30 @@ public final class MapboxMap {
return cameraPosition;
}
+ /**
+ * Get a camera position that fits a provided shape with a given bearing and padding.
+ *
+ * @param geometry the geometry to constrain the map with
+ * @param bearing the bearing at which to compute the geometry's bounds
+ * @param padding the padding to apply to the bounds
+ * @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;
+ }
+
//
// Padding
//
@@ -1639,7 +1758,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked on every camera change position.
* To unset the callback, use null.
*/
- @UiThread
@Deprecated
public void setOnCameraChangeListener(@Nullable OnCameraChangeListener listener) {
transform.setOnCameraChangeListener(listener);
@@ -1649,51 +1767,131 @@ public final class MapboxMap {
* Sets a callback that is invoked when camera movement has ended.
*
* @param listener the listener to notify
+ * @deprecated use {@link #addOnCameraIdleListener(OnCameraIdleListener)}
+ * and {@link #removeOnCameraIdleListener(OnCameraIdleListener)} instead
*/
- @UiThread
+ @Deprecated
public void setOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
cameraChangeDispatcher.setOnCameraIdleListener(listener);
}
/**
+ * Adds a callback that is invoked when camera movement has ended.
+ *
+ * @param listener the listener to notify
+ */
+ public void addOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
+ cameraChangeDispatcher.addOnCameraIdleListener(listener);
+ }
+
+ /**
+ * Removes a callback that is invoked when camera movement has ended.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
+ cameraChangeDispatcher.removeOnCameraIdleListener(listener);
+ }
+
+ /**
* Sets a callback that is invoked when camera movement was cancelled.
*
* @param listener the listener to notify
+ * @deprecated use {@link #addOnCameraMoveCancelListener(OnCameraMoveCanceledListener)} and
+ * {@link #removeOnCameraMoveCancelListener(OnCameraMoveCanceledListener)} instead
*/
- @UiThread
+ @Deprecated
public void setOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.setOnCameraMoveCanceledListener(listener);
}
/**
+ * Adds a callback that is invoked when camera movement was cancelled.
+ *
+ * @param listener the listener to notify
+ */
+ public void addOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
+ cameraChangeDispatcher.addOnCameraMoveCancelListener(listener);
+ }
+
+ /**
+ * Removes a callback that is invoked when camera movement was cancelled.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
+ cameraChangeDispatcher.removeOnCameraMoveCancelListener(listener);
+ }
+
+ /**
* Sets a callback that is invoked when camera movement has started.
*
* @param listener the listener to notify
+ * @deprecated use {@link #addOnCameraMoveStartedListener(OnCameraMoveStartedListener)} and
+ * {@link #removeOnCameraMoveStartedListener(OnCameraMoveStartedListener)} instead
*/
- @UiThread
- public void setOnCameraMoveStartedistener(@Nullable OnCameraMoveStartedListener listener) {
+ @Deprecated
+ public void setOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.setOnCameraMoveStartedListener(listener);
}
/**
+ * Adds a callback that is invoked when camera movement has started.
+ *
+ * @param listener the listener to notify
+ */
+ public void addOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
+ cameraChangeDispatcher.addOnCameraMoveStartedListener(listener);
+ }
+
+ /**
+ * Removes a callback that is invoked when camera movement has started.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
+ cameraChangeDispatcher.removeOnCameraMoveStartedListener(listener);
+ }
+
+ /**
* Sets a callback that is invoked when camera position changes.
*
* @param listener the listener to notify
+ * @deprecated use {@link #addOnCameraMoveListener(OnCameraMoveListener)} and
+ * {@link #removeOnCameraMoveListener(OnCameraMoveListener)}instead
*/
- @UiThread
+ @Deprecated
public void setOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
cameraChangeDispatcher.setOnCameraMoveListener(listener);
}
/**
+ * Adds a callback that is invoked when camera position changes.
+ *
+ * @param listener the listener to notify
+ */
+ public void addOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
+ cameraChangeDispatcher.addOnCameraMoveListener(listener);
+ }
+
+ /**
+ * Removes a callback that is invoked when camera position changes.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
+ cameraChangeDispatcher.removeOnCameraMoveListener(listener);
+ }
+
+ /**
* Sets a callback that's invoked on every frame rendered to the map view.
*
* @param listener The callback that's invoked on every frame rendered to the map view.
* To unset the callback, use null.
*/
- @UiThread
public void setOnFpsChangedListener(@Nullable OnFpsChangedListener listener) {
onFpsChangedListener = listener;
+ nativeMapView.setOnFpsChangedListener(listener);
}
// used by MapView
@@ -1706,10 +1904,34 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the map is scrolled.
* To unset the callback, use null.
+ *
+ * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} instead.
*/
- @UiThread
+ @Deprecated
public void setOnScrollListener(@Nullable OnScrollListener listener) {
- onRegisterTouchListener.onRegisterScrollListener(listener);
+ onRegisterTouchListener.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);
+ }
+
+ /**
+ * 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);
}
/**
@@ -1717,10 +1939,32 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the map is flinged.
* To unset the callback, use null.
+ *
+ * @deprecated Use {@link #addOnFlingListener(OnFlingListener)} instead.
*/
- @UiThread
+ @Deprecated
public void setOnFlingListener(@Nullable OnFlingListener listener) {
- onRegisterTouchListener.onRegisterFlingListener(listener);
+ onRegisterTouchListener.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);
+ }
+
+ /**
+ * 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);
}
/**
@@ -1728,10 +1972,32 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the user clicks on the map view.
* To unset the callback, use null.
+ *
+ * @deprecated Use {@link #addOnMapClickListener(OnMapClickListener)} instead.
*/
- @UiThread
+ @Deprecated
public void setOnMapClickListener(@Nullable OnMapClickListener listener) {
- onRegisterTouchListener.onRegisterMapClickListener(listener);
+ onRegisterTouchListener.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);
+ }
+
+ /**
+ * 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);
}
/**
@@ -1739,10 +2005,32 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the user long clicks on the map view.
* To unset the callback, use null.
+ *
+ * @deprecated Use {@link #addOnMapLongClickListener(OnMapLongClickListener)} instead.
*/
- @UiThread
+ @Deprecated
public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) {
- onRegisterTouchListener.onRegisterMapLongClickListener(listener);
+ onRegisterTouchListener.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);
+ }
+
+ /**
+ * 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);
}
/**
@@ -1751,7 +2039,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when the user clicks on an info window.
* To unset the callback, use null.
*/
- @UiThread
public void setOnInfoWindowClickListener(@Nullable OnInfoWindowClickListener listener) {
annotationManager.getInfoWindowManager().setOnInfoWindowClickListener(listener);
}
@@ -1761,7 +2048,6 @@ public final class MapboxMap {
*
* @return Current active InfoWindow Click Listener
*/
- @UiThread
public OnInfoWindowClickListener getOnInfoWindowClickListener() {
return annotationManager.getInfoWindowManager().getOnInfoWindowClickListener();
}
@@ -1772,7 +2058,6 @@ public final class MapboxMap {
* @param listener The callback that's invoked when a marker's info window is long pressed. To unset the callback,
* use null.
*/
- @UiThread
public void setOnInfoWindowLongClickListener(@Nullable OnInfoWindowLongClickListener
listener) {
annotationManager.getInfoWindowManager().setOnInfoWindowLongClickListener(listener);
@@ -1801,7 +2086,6 @@ public final class MapboxMap {
*
* @return Current active InfoWindow Close Listener
*/
- @UiThread
public OnInfoWindowCloseListener getOnInfoWindowCloseListener() {
return annotationManager.getInfoWindowManager().getOnInfoWindowCloseListener();
}
@@ -1814,8 +2098,10 @@ public final class MapboxMap {
* 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/plugin-locationlayer instead.
*/
- @UiThread
+ @Deprecated
public boolean isMyLocationEnabled() {
return trackingSettings.isMyLocationEnabled();
}
@@ -1830,8 +2116,10 @@ public final class MapboxMap {
* 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/plugin-locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setMyLocationEnabled(boolean enabled) {
trackingSettings.setMyLocationEnabled(enabled);
}
@@ -1840,9 +2128,11 @@ public final class MapboxMap {
* 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/plugin-locationlayer instead.
*/
- @UiThread
@Nullable
+ @Deprecated
public Location getMyLocation() {
return trackingSettings.getMyLocation();
}
@@ -1853,8 +2143,10 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the user clicks on a marker.
* To unset the callback, use null.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener
listener) {
trackingSettings.setOnMyLocationChangeListener(listener);
@@ -1864,10 +2156,10 @@ public final class MapboxMap {
* Replaces the location source of the my-location layer.
*
* @param locationSource A {@link LocationEngine} location source to use in the my-location layer.
- * Set to null to use the default {@link LocationSource}
- * location source.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setLocationSource(@Nullable LocationEngine locationSource) {
trackingSettings.setLocationSource(locationSource);
}
@@ -1877,8 +2169,10 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the location tracking mode changes.
* To unset the callback, use null.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyLocationTrackingModeChangeListener(
@Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) {
trackingSettings.setOnMyLocationTrackingModeChangeListener(listener);
@@ -1889,8 +2183,10 @@ public final class MapboxMap {
*
* @param listener The callback that's invoked when the bearing tracking mode changes.
* To unset the callback, use null.
+ * @deprecated use location layer plugin from
+ * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugin-locationlayer instead.
*/
- @UiThread
+ @Deprecated
public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) {
trackingSettings.setOnMyBearingTrackingModeChangeListener(listener);
}
@@ -1904,7 +2200,6 @@ public final class MapboxMap {
*
* @param callback Callback method invoked when the snapshot is taken.
*/
- @UiThread
public void snapshot(@NonNull SnapshotReadyCallback callback) {
nativeMapView.addSnapshotCallback(callback);
}
@@ -1916,7 +2211,6 @@ public final class MapboxMap {
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
- @UiThread
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String...
layerIds) {
@@ -1931,7 +2225,6 @@ public final class MapboxMap {
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
- @UiThread
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates,
@Nullable Filter.Statement filter,
@@ -1946,7 +2239,6 @@ public final class MapboxMap {
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
- @UiThread
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates,
@Nullable String... layerIds) {
@@ -1961,7 +2253,6 @@ public final class MapboxMap {
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
- @UiThread
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates,
@Nullable Filter.Statement filter,
@@ -1969,6 +2260,15 @@ public final class MapboxMap {
return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter);
}
+ FocalPointChangeListener createFocalPointChangeListener() {
+ return new FocalPointChangeListener() {
+ @Override
+ public void onFocalPointChanged(PointF pointF) {
+ focalPoint = pointF;
+ }
+ };
+ }
+
//
// Interfaces
//
@@ -2067,6 +2367,21 @@ public final class MapboxMap {
}
/**
+ * Interface definition for a callback to be invoked for when the compass is animating.
+ */
+ public interface OnCompassAnimationListener {
+ /**
+ * Called repeatedly as the compass continues to move after clicking on it.
+ */
+ void onCompassAnimation();
+
+ /**
+ * Called when compass animation has ended.
+ */
+ void onCompassAnimationFinished();
+ }
+
+ /**
* Interface definition for a callback to be invoked when a frame is rendered to the map view.
*
* @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener)
@@ -2085,13 +2400,29 @@ public final class MapboxMap {
* related to touch and click events.
*/
interface OnRegisterTouchListener {
- void onRegisterMapClickListener(OnMapClickListener listener);
+ void onSetMapClickListener(OnMapClickListener listener);
+
+ void onAddMapClickListener(OnMapClickListener listener);
+
+ void onRemoveMapClickListener(OnMapClickListener listener);
- void onRegisterMapLongClickListener(OnMapLongClickListener listener);
+ void onSetMapLongClickListener(OnMapLongClickListener listener);
- void onRegisterScrollListener(OnScrollListener listener);
+ void onAddMapLongClickListener(OnMapLongClickListener listener);
- void onRegisterFlingListener(OnFlingListener listener);
+ void onRemoveMapLongClickListener(OnMapLongClickListener listener);
+
+ void onSetScrollListener(OnScrollListener listener);
+
+ void onAddScrollListener(OnScrollListener listener);
+
+ void onRemoveScrollListener(OnScrollListener listener);
+
+ void onSetFlingListener(OnFlingListener listener);
+
+ void onAddFlingListener(OnFlingListener listener);
+
+ void onRemoveFlingListener(OnFlingListener listener);
}
/**
@@ -2138,6 +2469,34 @@ public final class MapboxMap {
}
/**
+ * Interface definition for a callback to be invoked when the user clicks on a polygon.
+ *
+ * @see MapboxMap#setOnPolygonClickListener(OnPolygonClickListener)
+ */
+ public interface OnPolygonClickListener {
+ /**
+ * Called when the user clicks on a polygon.
+ *
+ * @param polygon The polygon the user clicked on.
+ */
+ void onPolygonClick(@NonNull Polygon polygon);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the user clicks on a polyline.
+ *
+ * @see MapboxMap#setOnPolylineClickListener(OnPolylineClickListener)
+ */
+ public interface OnPolylineClickListener {
+ /**
+ * Called when the user clicks on a polyline.
+ *
+ * @param polyline The polyline the user clicked on.
+ */
+ void onPolylineClick(@NonNull Polyline polyline);
+ }
+
+ /**
* Interface definition for a callback to be invoked when the user clicks on an info window.
*
* @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener)
@@ -2203,7 +2562,10 @@ public final class MapboxMap {
* Interface definition for a callback to be invoked when an MarkerView will be shown.
*
* @param <U> the instance type of MarkerView
+ * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android
+ * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
*/
+ @Deprecated
public abstract static class MarkerViewAdapter<U extends MarkerView> {
private Context context;
@@ -2405,11 +2767,13 @@ public final class MapboxMap {
}
/**
- * Interface definintion for a callback to be invoked when the style has finished loading.
+ * Interface definition for a callback to be invoked when the style has finished loading.
*/
public interface OnStyleLoadedListener {
/**
- * Invoked when the style has finished loading.
+ * Invoked when the style has finished loading
+ *
+ * @param style the style that has been loaded
*/
void onStyleLoaded(String style);
}
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 98f94ddb39..2719d7f016 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
@@ -3,8 +3,6 @@ package com.mapbox.mapboxsdk.maps;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
@@ -21,6 +19,7 @@ import android.view.Gravity;
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;
@@ -37,6 +36,7 @@ public class MapboxMapOptions implements Parcelable {
private static final float FOUR_DP = 4f;
private static final float NINETY_TWO_DP = 92f;
+ private static final int UNDEFINED_COLOR = -1;
private CameraPosition cameraPosition;
@@ -53,7 +53,7 @@ public class MapboxMapOptions implements Parcelable {
private int[] logoMargins;
@ColorInt
- private int attributionTintColor = -1;
+ private int attributionTintColor = UNDEFINED_COLOR;
private boolean attributionEnabled = true;
private int attributionGravity = Gravity.BOTTOM;
private int[] attributionMargins;
@@ -72,15 +72,20 @@ public class MapboxMapOptions implements Parcelable {
private Drawable myLocationForegroundDrawable;
private Drawable myLocationForegroundBearingDrawable;
private Drawable myLocationBackgroundDrawable;
- private int myLocationForegroundTintColor;
- private int myLocationBackgroundTintColor;
+ @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;
private String apiBaseUrl;
- @Deprecated
private boolean textureMode;
private String style;
@@ -146,23 +151,14 @@ public class MapboxMapOptions implements Parcelable {
myLocationBackgroundPadding = in.createIntArray();
myLocationAccuracyAlpha = in.readInt();
myLocationAccuracyTintColor = in.readInt();
+ myLocationAccuracyThreshold = in.readFloat();
style = in.readString();
apiBaseUrl = in.readString();
textureMode = in.readByte() != 0;
- }
-
- static Bitmap getBitmapFromDrawable(Drawable drawable) {
- if (drawable instanceof BitmapDrawable) {
- return ((BitmapDrawable) drawable).getBitmap();
- } else {
- Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- return bitmap;
- }
+ prefetchesTiles = in.readByte() != 0;
+ zMediaOverlay = in.readByte() != 0;
+ localIdeographFontFamily = in.readString();
}
/**
@@ -234,7 +230,7 @@ public class MapboxMapOptions implements Parcelable {
FOUR_DP * pxlRatio))});
mapboxMapOptions.attributionTintColor(typedArray.getColor(
- R.styleable.mapbox_MapView_mapbox_uiAttributionTintColor, -1));
+ R.styleable.mapbox_MapView_mapbox_uiAttributionTintColor, UNDEFINED_COLOR));
mapboxMapOptions.attributionEnabled(typedArray.getBoolean(
R.styleable.mapbox_MapView_mapbox_uiAttribution, true));
mapboxMapOptions.attributionGravity(typedArray.getInt(
@@ -251,10 +247,9 @@ public class MapboxMapOptions implements Parcelable {
mapboxMapOptions.locationEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_myLocation, false));
mapboxMapOptions.myLocationForegroundTintColor(
- typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationTintColor,
- ColorUtils.getPrimaryColor(context)));
+ typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationTintColor, UNDEFINED_COLOR));
mapboxMapOptions.myLocationBackgroundTintColor(
- typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundTintColor, Color.WHITE));
+ typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundTintColor, UNDEFINED_COLOR));
Drawable foregroundDrawable = typedArray.getDrawable(R.styleable.mapbox_MapView_mapbox_myLocationDrawable);
if (foregroundDrawable == null) {
@@ -290,8 +285,16 @@ public class MapboxMapOptions implements Parcelable {
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.setPrefetchesTiles(
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true));
+ mapboxMapOptions.renderSurfaceOnTop(
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableZMediaOverlay, false));
+ mapboxMapOptions.localIdeographFontFamily(
+ typedArray.getString(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamily));
} finally {
typedArray.recycle();
}
@@ -378,7 +381,7 @@ public class MapboxMapOptions implements Parcelable {
/**
* Specifies the gravity state of mapbox_compass_icon for a map view.
*
- * @param gravity see {@link android.view.Gravity}
+ * @param gravity Android SDK Gravity.
* @return This
*/
public MapboxMapOptions compassGravity(int gravity) {
@@ -439,7 +442,7 @@ public class MapboxMapOptions implements Parcelable {
/**
* Specifies the gravity state of logo for a map view.
*
- * @param gravity see {@link android.view.Gravity}
+ * @param gravity Android SDK Gravity.
* @return This
*/
public MapboxMapOptions logoGravity(int gravity) {
@@ -472,7 +475,7 @@ public class MapboxMapOptions implements Parcelable {
/**
* Specifies the gravity state of attribution for a map view.
*
- * @param gravity see {@link android.view.Gravity}
+ * @param gravity Android SDK Gravity.
* @return This
*/
public MapboxMapOptions attributionGravity(int gravity) {
@@ -638,7 +641,7 @@ public class MapboxMapOptions implements Parcelable {
/**
* Set the background tint color of MyLocationView.
*
- * @param myLocationBackgroundTintColor the color to tint the background
+ * @param myLocationBackgroundTintColor the color to tint the background drawable
* @return This
*/
public MapboxMapOptions myLocationBackgroundTintColor(@ColorInt int myLocationBackgroundTintColor) {
@@ -680,15 +683,28 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * Enable TextureView as rendered surface.
+ * 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 4.2.0 release we replaced our TextureView with an SurfaceView implemenation.
- * Enabling this option will use the deprecated TextureView instead.
+ * Since the 5.2.0 release we replaced our TextureView with an {@link android.opengl.GLSurfaceView}
+ * implementation. Enabling this option will use the {@link android.view.TextureView} instead.
+ * {@link android.view.TextureView} can be useful in situations where you need to animate, scale
+ * or transform the view. This comes at a siginficant performance penalty and should not be considered
+ * unless absolutely needed.
* </p>
*
* @param textureMode True to enable texture mode
* @return This
- * @deprecated As of the 4.2.0 release, using TextureView is deprecated.
*/
public MapboxMapOptions textureMode(boolean textureMode) {
this.textureMode = textureMode;
@@ -696,6 +712,59 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render
+ * a low resolution preview while more detailed tiles are loaded.
+ * Enabled by default
+ *
+ * @param enable true to enable
+ * @return This
+ */
+ public MapboxMapOptions setPrefetchesTiles(boolean enable) {
+ this.prefetchesTiles = enable;
+ return this;
+ }
+
+ /**
+ * Set the font-family for generating glyphs locally for ideographs in the ‘CJK Unified Ideographs’
+ * and ‘Hangul Syllables’ ranges.
+ *
+ * @param fontFamily font family for local ideograph generation.
+ * @return This
+ */
+ public MapboxMapOptions localIdeographFontFamily(String fontFamily) {
+ this.localIdeographFontFamily = fontFamily;
+ return this;
+ }
+
+ /**
+ * Check whether tile pre-fetching is enabled.
+ *
+ * @return true if enabled
+ */
+ public boolean getPrefetchesTiles() {
+ return prefetchesTiles;
+ }
+
+
+ /**
+ * Set the flag to render the map surface on top of another surface.
+ *
+ * @param renderOnTop true if this map is shown on top of another one, false if bottom.
+ */
+ public void renderSurfaceOnTop(boolean renderOnTop) {
+ this.zMediaOverlay = renderOnTop;
+ }
+
+ /**
+ * Get the flag to render the map surface on top of another surface.
+ *
+ * @return true if this map is
+ */
+ public boolean getRenderSurfaceOnTop() {
+ return zMediaOverlay;
+ }
+
+ /**
* Get the current configured API endpoint base URL.
*
* @return Base URL to be used API endpoint.
@@ -944,6 +1013,7 @@ public class MapboxMapOptions implements Parcelable {
*
* @return the tint color
*/
+ @ColorInt
public int getMyLocationForegroundTintColor() {
return myLocationForegroundTintColor;
}
@@ -953,6 +1023,7 @@ public class MapboxMapOptions implements Parcelable {
*
* @return the tint color
*/
+ @ColorInt
public int getMyLocationBackgroundTintColor() {
return myLocationBackgroundTintColor;
}
@@ -985,6 +1056,15 @@ public class MapboxMapOptions implements Parcelable {
}
/**
+ * 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.
@@ -994,15 +1074,24 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * Returns true if TextureView is being used a render view.
+ * Returns true if TextureView is being used the render view.
*
* @return True if TextureView is used.
- * @deprecated As of the 4.2.0 release, using TextureView is deprecated.
*/
public boolean getTextureMode() {
return textureMode;
}
+ /**
+ * Returns the font-family for locally overriding generation of glyphs in the
+ * ‘CJK Unified Ideographs’ and ‘Hangul Syllables’ ranges.
+ *
+ * @return Local ideograph font family name.
+ */
+ public String getLocalIdeographFontFamily() {
+ return localIdeographFontFamily;
+ }
+
public static final Parcelable.Creator<MapboxMapOptions> CREATOR = new Parcelable.Creator<MapboxMapOptions>() {
public MapboxMapOptions createFromParcel(Parcel in) {
return new MapboxMapOptions(in);
@@ -1028,7 +1117,7 @@ public class MapboxMapOptions implements Parcelable {
dest.writeIntArray(compassMargins);
dest.writeByte((byte) (fadeCompassFacingNorth ? 1 : 0));
dest.writeParcelable(compassImage != null
- ? getBitmapFromDrawable(compassImage) : null, flags);
+ ? BitmapUtils.getBitmapFromDrawable(compassImage) : null, flags);
dest.writeByte((byte) (logoEnabled ? 1 : 0));
dest.writeInt(logoGravity);
@@ -1052,20 +1141,24 @@ public class MapboxMapOptions implements Parcelable {
dest.writeByte((byte) (myLocationEnabled ? 1 : 0));
dest.writeParcelable(myLocationForegroundDrawable != null
- ? getBitmapFromDrawable(myLocationForegroundDrawable) : null, flags);
+ ? BitmapUtils.getBitmapFromDrawable(myLocationForegroundDrawable) : null, flags);
dest.writeParcelable(myLocationForegroundBearingDrawable != null
- ? getBitmapFromDrawable(myLocationForegroundBearingDrawable) : null, flags);
+ ? BitmapUtils.getBitmapFromDrawable(myLocationForegroundBearingDrawable) : null, flags);
dest.writeParcelable(myLocationBackgroundDrawable != null
- ? getBitmapFromDrawable(myLocationBackgroundDrawable) : null, flags);
+ ? 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) (prefetchesTiles ? 1 : 0));
+ dest.writeByte((byte) (zMediaOverlay ? 1 : 0));
+ dest.writeString(localIdeographFontFamily);
}
@Override
@@ -1150,6 +1243,9 @@ public class MapboxMapOptions implements Parcelable {
if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) {
return false;
}
+ if (myLocationAccuracyThreshold != options.myLocationAccuracyThreshold) {
+ return false;
+ }
if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null) {
return false;
}
@@ -1186,6 +1282,16 @@ public class MapboxMapOptions implements Parcelable {
if (apiBaseUrl != null ? !apiBaseUrl.equals(options.apiBaseUrl) : options.apiBaseUrl != null) {
return false;
}
+ if (prefetchesTiles != options.prefetchesTiles) {
+ return false;
+ }
+ if (zMediaOverlay != options.zMediaOverlay) {
+ return false;
+ }
+ if (localIdeographFontFamily != options.localIdeographFontFamily) {
+ return false;
+ }
+
return false;
}
@@ -1227,9 +1333,14 @@ public class MapboxMapOptions implements Parcelable {
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 + (style != null ? style.hashCode() : 0);
+ result = 31 * result + (prefetchesTiles ? 1 : 0);
+ result = 31 * result + (zMediaOverlay ? 1 : 0);
+ result = 31 * result + (localIdeographFontFamily != null ? localIdeographFontFamily.hashCode() : 0);
return result;
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
index 306ad59b8d..2c2f07a112 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
@@ -18,8 +18,6 @@ import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import java.util.ArrayList;
import java.util.List;
-import timber.log.Timber;
-
/**
* Encapsulates {@link Marker}'s functionality.
*/
@@ -55,7 +53,7 @@ class MarkerContainer implements Markers {
mapboxMap) {
int count = markerOptionsList.size();
List<Marker> markers = new ArrayList<>(count);
- if (count > 0) {
+ if (nativeMapView != null && count > 0) {
BaseMarkerOptions markerOptions;
Marker marker;
for (int i = 0; i < count; i++) {
@@ -65,26 +63,13 @@ class MarkerContainer implements Markers {
}
if (markers.size() > 0) {
- long[] ids = null;
- if (nativeMapView != null) {
- ids = nativeMapView.addMarkers(markers);
- }
-
- long id = 0;
- Marker m;
- for (int i = 0; i < markers.size(); i++) {
- m = markers.get(i);
- m.setMapboxMap(mapboxMap);
- if (ids != null) {
- id = ids[i];
- } else {
- // unit test
- id++;
- }
- m.setId(id);
- annotations.put(id, m);
+ long[] ids = nativeMapView.addMarkers(markers);
+ for (int i = 0; i < ids.length; i++) {
+ Marker createdMarker = markers.get(i);
+ createdMarker.setMapboxMap(mapboxMap);
+ createdMarker.setId(ids[i]);
+ annotations.put(ids[i], createdMarker);
}
-
}
}
return markers;
@@ -92,11 +77,6 @@ class MarkerContainer implements Markers {
@Override
public void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) {
- if (!isAddedToMap(updatedMarker)) {
- Timber.w("Attempting to update non-added Marker with value %s", updatedMarker);
- return;
- }
-
ensureIconLoaded(updatedMarker, mapboxMap);
nativeMapView.updateMarker(updatedMarker);
annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker);
@@ -118,15 +98,8 @@ class MarkerContainer implements Markers {
@NonNull
@Override
public List<Marker> obtainAllIn(@NonNull RectF rectangle) {
- // convert Rectangle to be density depedent
- float pixelRatio = nativeMapView.getPixelRatio();
- RectF rect = new RectF(rectangle.left / pixelRatio,
- rectangle.top / pixelRatio,
- rectangle.right / pixelRatio,
- rectangle.bottom / pixelRatio);
-
+ RectF rect = nativeMapView.getDensityDependantRectangle(rectangle);
long[] ids = nativeMapView.queryPointAnnotations(rect);
-
List<Long> idsList = new ArrayList<>(ids.length);
for (long id : ids) {
idsList.add(id);
@@ -237,10 +210,6 @@ class MarkerContainer implements Markers {
return marker;
}
- private boolean isAddedToMap(Annotation annotation) {
- return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) != -1;
- }
-
private void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) {
if (!(marker instanceof MarkerView)) {
iconManager.ensureIconLoaded(marker, mapboxMap);
@@ -261,6 +230,7 @@ class MarkerContainer implements Markers {
if (icon == null) {
icon = IconFactory.getInstance(mapView.getContext()).defaultMarkerView();
}
+ iconManager.loadIconForMarkerView(marker);
marker.setIcon(icon);
return marker;
}
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 c0586a6449..f1635c898f 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
@@ -1,17 +1,15 @@
package com.mapbox.mapboxsdk.maps;
-import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.RectF;
-import android.os.Build;
+import android.os.AsyncTask;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
-import android.view.Surface;
import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.annotations.Icon;
@@ -19,10 +17,10 @@ import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
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.layers.CannotAddLayerException;
import com.mapbox.mapboxsdk.style.layers.Filter;
@@ -32,12 +30,14 @@ 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;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.Map;
import timber.log.Timber;
@@ -56,12 +56,12 @@ final class NativeMapView {
//Hold a reference to prevent it from being GC'd as long as it's used on the native side
private final FileSource fileSource;
+ // Used to schedule work on the MapRenderer Thread
+ private MapRenderer mapRenderer;
+
// Device density
private final float pixelRatio;
- // Listeners for Map change events
- private CopyOnWriteArrayList<MapView.OnMapChangedListener> onMapChangedListeners;
-
// Listener invoked to return a bitmap of the map
private MapboxMap.SnapshotReadyCallback snapshotReadyCallback;
@@ -73,32 +73,15 @@ final class NativeMapView {
// Constructors
//
- public NativeMapView(MapView mapView) {
+ public NativeMapView(final MapView mapView, MapRenderer mapRenderer) {
+ this.mapRenderer = mapRenderer;
+ this.mapView = mapView;
+
Context context = mapView.getContext();
fileSource = FileSource.getInstance(context);
-
pixelRatio = context.getResources().getDisplayMetrics().density;
- int availableProcessors = Runtime.getRuntime().availableProcessors();
- ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
- ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- activityManager.getMemoryInfo(memoryInfo);
- long totalMemory = memoryInfo.availMem;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- totalMemory = memoryInfo.totalMem;
- }
-
- if (availableProcessors < 0) {
- throw new IllegalArgumentException("availableProcessors cannot be negative.");
- }
-
- if (totalMemory < 0) {
- throw new IllegalArgumentException("totalMemory cannot be negative.");
- }
- onMapChangedListeners = new CopyOnWriteArrayList<>();
- this.mapView = mapView;
- String programCacheDir = context.getCacheDir().getAbsolutePath();
- nativeInitialize(this, fileSource, pixelRatio, programCacheDir, availableProcessors, totalMemory);
+ nativeInitialize(this, fileSource, mapRenderer, pixelRatio);
}
//
@@ -107,9 +90,10 @@ final class NativeMapView {
private boolean isDestroyedOn(String callingMethod) {
if (destroyed && !TextUtils.isEmpty(callingMethod)) {
- Timber.e(String.format(MapboxConstants.MAPBOX_LOCALE,
+ Timber.e(
"You're calling `%s` after the `MapView` was destroyed, were you invoking it after `onDestroy()`?",
- callingMethod));
+ callingMethod
+ );
}
return destroyed;
}
@@ -120,60 +104,12 @@ final class NativeMapView {
destroyed = true;
}
- public void initializeDisplay() {
- if (isDestroyedOn("initializeDisplay")) {
- return;
- }
- nativeInitializeDisplay();
- }
-
- public void terminateDisplay() {
- if (isDestroyedOn("terminateDisplay")) {
- return;
- }
- nativeTerminateDisplay();
- }
-
- public void initializeContext() {
- if (isDestroyedOn("initializeContext")) {
- return;
- }
- nativeInitializeContext();
- }
-
- public void terminateContext() {
- if (isDestroyedOn("terminateContext")) {
- return;
- }
- nativeTerminateContext();
- }
-
- public void createSurface(Surface surface) {
- if (isDestroyedOn("createSurface")) {
- return;
- }
- nativeCreateSurface(surface);
- }
-
- public void destroySurface() {
- if (isDestroyedOn("destroySurface")) {
- return;
- }
- nativeDestroySurface();
- }
-
public void update() {
if (isDestroyedOn("update")) {
return;
}
- nativeUpdate();
- }
- public void render() {
- if (isDestroyedOn("render")) {
- return;
- }
- nativeRender();
+ mapRenderer.requestRender();
}
public void resizeView(int width, int height) {
@@ -194,41 +130,18 @@ final class NativeMapView {
if (width > 65535) {
// we have seen edge cases where devices return incorrect values #6111
Timber.e("Device returned an out of range width size, "
- + "capping value at 65535 instead of " + width);
+ + "capping value at 65535 instead of %s", width);
width = 65535;
}
if (height > 65535) {
// we have seen edge cases where devices return incorrect values #6111
Timber.e("Device returned an out of range height size, "
- + "capping value at 65535 instead of " + height);
+ + "capping value at 65535 instead of %s", height);
height = 65535;
}
- nativeResizeView(width, height);
- }
-
- public void resizeFramebuffer(int fbWidth, int fbHeight) {
- if (isDestroyedOn("resizeFramebuffer")) {
- return;
- }
- if (fbWidth < 0) {
- throw new IllegalArgumentException("fbWidth cannot be negative.");
- }
- if (fbHeight < 0) {
- throw new IllegalArgumentException("fbHeight cannot be negative.");
- }
-
- if (fbWidth > 65535) {
- throw new IllegalArgumentException(
- "fbWidth cannot be greater than 65535.");
- }
-
- if (fbHeight > 65535) {
- throw new IllegalArgumentException(
- "fbHeight cannot be greater than 65535.");
- }
- nativeResizeFramebuffer(fbWidth, fbHeight);
+ nativeResizeView(width, height);
}
public void setStyleUrl(String url) {
@@ -323,6 +236,13 @@ final class NativeMapView {
return nativeGetCameraForLatLngBounds(latLngBounds);
}
+ public CameraPosition getCameraForGeometry(Geometry geometry, double bearing) {
+ if (isDestroyedOn("getCameraForGeometry")) {
+ return null;
+ }
+ return nativeGetCameraForGeometry(geometry, bearing);
+ }
+
public void resetPosition() {
if (isDestroyedOn("resetPosition")) {
return;
@@ -551,6 +471,13 @@ final class NativeMapView {
return nativeQueryPointAnnotations(rect);
}
+ public long[] queryShapeAnnotations(RectF rectF) {
+ if (isDestroyedOn("queryShapeAnnotations")) {
+ return new long[] {};
+ }
+ return nativeQueryShapeAnnotations(rectF);
+ }
+
public void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels) {
if (isDestroyedOn("addAnnotationIcon")) {
return;
@@ -558,6 +485,13 @@ final class NativeMapView {
nativeAddAnnotationIcon(symbol, width, height, scale, pixels);
}
+ public void removeAnnotationIcon(String symbol) {
+ if (isDestroyedOn("removeAnnotationIcon")) {
+ return;
+ }
+ nativeRemoveAnnotationIcon(symbol);
+ }
+
public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) {
if (isDestroyedOn("setVisibleCoordinateBounds")) {
return;
@@ -593,13 +527,6 @@ final class NativeMapView {
return nativeGetDebug();
}
- public void setEnableFps(boolean enable) {
- if (isDestroyedOn("setEnableFps")) {
- return;
- }
- nativeSetEnableFps(enable);
- }
-
public boolean isFullyLoaded() {
if (isDestroyedOn("isFullyLoaded")) {
return false;
@@ -689,6 +616,20 @@ final class NativeMapView {
return nativeGetCameraPosition();
}
+ public void setPrefetchesTiles(boolean enable) {
+ if (isDestroyedOn("setPrefetchesTiles")) {
+ return;
+ }
+ nativeSetPrefetchesTiles(enable);
+ }
+
+ public boolean getPrefetchesTiles() {
+ if (isDestroyedOn("getPrefetchesTiles")) {
+ return false;
+ }
+ return nativeGetPrefetchesTiles();
+ }
+
// Runtime style Api
public long getTransitionDuration() {
@@ -792,7 +733,7 @@ final class NativeMapView {
if (isDestroyedOn("addSource")) {
return;
}
- nativeAddSource(source.getNativePtr());
+ nativeAddSource(source, source.getNativePtr());
}
@Nullable
@@ -800,14 +741,15 @@ final class NativeMapView {
if (isDestroyedOn("removeSource")) {
return null;
}
- return nativeRemoveSourceById(sourceId);
+ Source source = getSource(sourceId);
+ return removeSource(source);
}
public Source removeSource(@NonNull Source source) {
if (isDestroyedOn("removeSource")) {
return null;
}
- nativeRemoveSource(source.getNativePtr());
+ nativeRemoveSource(source, source.getNativePtr());
return source;
}
@@ -815,6 +757,7 @@ final class NativeMapView {
if (isDestroyedOn("addImage")) {
return;
}
+
// Check/correct config
if (image.getConfig() != Bitmap.Config.ARGB_8888) {
image = image.copy(Bitmap.Config.ARGB_8888, false);
@@ -831,6 +774,14 @@ final class NativeMapView {
nativeAddImage(name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array());
}
+ public void addImages(@NonNull HashMap<String, Bitmap> bitmapHashMap) {
+ if (isDestroyedOn("addImages")) {
+ return;
+ }
+ //noinspection unchecked
+ new BitmapImageConversionTask(this).execute(bitmapHashMap);
+ }
+
public void removeImage(String name) {
if (isDestroyedOn("removeImage")) {
return;
@@ -838,6 +789,13 @@ final class NativeMapView {
nativeRemoveImage(name);
}
+ public Bitmap getImage(String name) {
+ if (isDestroyedOn("getImage")) {
+ return null;
+ }
+ return nativeGetImage(name);
+ }
+
// Feature querying
@NonNull
@@ -869,13 +827,6 @@ final class NativeMapView {
return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
}
- public void scheduleTakeSnapshot() {
- if (isDestroyedOn("scheduleTakeSnapshot")) {
- return;
- }
- nativeTakeSnapshot();
- }
-
public void setApiBaseUrl(String baseUrl) {
if (isDestroyedOn("setApiBaseUrl")) {
return;
@@ -894,33 +845,23 @@ final class NativeMapView {
return pixelRatio;
}
+ RectF getDensityDependantRectangle(final RectF rectangle) {
+ return new RectF(
+ rectangle.left / pixelRatio,
+ rectangle.top / pixelRatio,
+ rectangle.right / pixelRatio,
+ rectangle.bottom / pixelRatio
+ );
+ }
+
//
// Callbacks
//
- protected void onInvalidate() {
- if (mapView != null) {
- mapView.onInvalidate();
- }
- }
-
protected void onMapChanged(int rawChange) {
- if (onMapChangedListeners != null) {
- for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) {
- try {
- onMapChangedListener.onMapChanged(rawChange);
- } catch (RuntimeException err) {
- Timber.e("Exception (%s) in MapView.OnMapChangedListener: %s", err.getClass(), err.getMessage());
- }
- }
- }
- }
-
- protected void onFpsChanged(double fps) {
- if (isDestroyedOn("OnFpsChanged")) {
- return;
+ if (mapView != null) {
+ mapView.onMapChange(rawChange);
}
- mapView.onFpsChanged(fps);
}
protected void onSnapshotReady(Bitmap mapContent) {
@@ -940,33 +881,13 @@ final class NativeMapView {
private native void nativeInitialize(NativeMapView nativeMapView,
FileSource fileSource,
- float pixelRatio,
- String programCacheDir,
- int availableProcessors,
- long totalMemory);
+ MapRenderer mapRenderer,
+ float pixelRatio);
private native void nativeDestroy();
- private native void nativeInitializeDisplay();
-
- private native void nativeTerminateDisplay();
-
- private native void nativeInitializeContext();
-
- private native void nativeTerminateContext();
-
- private native void nativeCreateSurface(Object surface);
-
- private native void nativeDestroySurface();
-
- private native void nativeUpdate();
-
- private native void nativeRender();
-
private native void nativeResizeView(int width, int height);
- private native void nativeResizeFramebuffer(int fbWidth, int fbHeight);
-
private native void nativeSetStyleUrl(String url);
private native String nativeGetStyleUrl();
@@ -989,6 +910,8 @@ final class NativeMapView {
private native CameraPosition nativeGetCameraForLatLngBounds(LatLngBounds latLngBounds);
+ private native CameraPosition nativeGetCameraForGeometry(Geometry geometry, double bearing);
+
private native void nativeResetPosition();
private native double nativeGetPitch();
@@ -1033,8 +956,12 @@ final class NativeMapView {
private native long[] nativeQueryPointAnnotations(RectF rect);
+ private native long[] nativeQueryShapeAnnotations(RectF rect);
+
private native void nativeAddAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels);
+ private native void nativeRemoveAnnotationIcon(String symbol);
+
private native void nativeSetVisibleCoordinateBounds(LatLng[] coordinates, RectF padding,
double direction, long duration);
@@ -1046,8 +973,6 @@ final class NativeMapView {
private native boolean nativeGetDebug();
- private native void nativeSetEnableFps(boolean enable);
-
private native boolean nativeIsFullyLoaded();
private native void nativeSetReachability(boolean status);
@@ -1103,17 +1028,19 @@ 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 nativeAddImages(Image[] images);
+
private native void nativeRemoveImage(String name);
+ private native Bitmap nativeGetImage(String name);
+
private native void nativeUpdatePolygon(long polygonId, Polygon polygon);
private native void nativeUpdatePolyline(long polylineId, Polyline polyline);
@@ -1131,6 +1058,10 @@ final class NativeMapView {
private native Light nativeGetLight();
+ private native void nativeSetPrefetchesTiles(boolean enable);
+
+ private native boolean nativeGetPrefetchesTiles();
+
int getWidth() {
if (isDestroyedOn("")) {
return 0;
@@ -1150,11 +1081,13 @@ final class NativeMapView {
//
void addOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) {
- onMapChangedListeners.add(listener);
+ if (mapView != null) {
+ mapView.addOnMapChangedListener(listener);
+ }
}
void removeOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) {
- onMapChangedListeners.remove(listener);
+ mapView.removeOnMapChangedListener(listener);
}
//
@@ -1162,8 +1095,86 @@ final class NativeMapView {
//
void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback) {
+ if (isDestroyedOn("addSnapshotCallback")) {
+ return;
+ }
snapshotReadyCallback = callback;
- scheduleTakeSnapshot();
- render();
+ nativeTakeSnapshot();
+ }
+
+ public void setOnFpsChangedListener(final MapboxMap.OnFpsChangedListener listener) {
+ mapRenderer.queueEvent(new Runnable() {
+
+ @Override
+ public void run() {
+ mapRenderer.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() {
+
+ @Override
+ public void onFpsChanged(final double fps) {
+ mapView.post(new Runnable() {
+
+ @Override
+ public void run() {
+ listener.onFpsChanged(fps);
+ }
+
+ });
+ }
+
+ });
+ }
+
+ });
+ }
+
+
+ //
+ // Image conversion
+ //
+
+ private static class BitmapImageConversionTask extends AsyncTask<HashMap<String, Bitmap>, Void, List<Image>> {
+
+ private NativeMapView nativeMapView;
+
+ BitmapImageConversionTask(NativeMapView nativeMapView) {
+ this.nativeMapView = nativeMapView;
+ }
+
+ @Override
+ protected List<Image> doInBackground(HashMap<String, Bitmap>... params) {
+ HashMap<String, Bitmap> bitmapHashMap = params[0];
+
+ List<Image> images = new ArrayList<>();
+ ByteBuffer buffer;
+ String name;
+ Bitmap bitmap;
+
+ for (Map.Entry<String, Bitmap> stringBitmapEntry : bitmapHashMap.entrySet()) {
+ name = stringBitmapEntry.getKey();
+ bitmap = stringBitmapEntry.getValue();
+
+ if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
+ bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false);
+ }
+
+ buffer = ByteBuffer.allocate(bitmap.getByteCount());
+ bitmap.copyPixelsToBuffer(buffer);
+
+ float density = bitmap.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : bitmap.getDensity();
+ float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT;
+
+ images.add(new Image(buffer.array(), pixelRatio, name, bitmap.getWidth(), bitmap.getHeight()));
+ }
+
+ return images;
+ }
+
+ @Override
+ protected void onPostExecute(List<Image> images) {
+ super.onPostExecute(images);
+ if (nativeMapView != null && !nativeMapView.isDestroyedOn("nativeAddImages")) {
+ nativeMapView.nativeAddImages(images.toArray(new Image[images.size()]));
+ }
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
index bcb94a975c..016862bddc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
@@ -11,8 +11,6 @@ import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import java.util.ArrayList;
import java.util.List;
-import timber.log.Timber;
-
/**
* Encapsulates {@link Polygon}'s functionality.
*/
@@ -44,7 +42,7 @@ class PolygonContainer implements Polygons {
Polygon polygon;
List<Polygon> polygons = new ArrayList<>(count);
- if (count > 0) {
+ if (nativeMapView != null && count > 0) {
for (PolygonOptions polygonOptions : polygonOptionsList) {
polygon = polygonOptions.getPolygon();
if (!polygon.getPoints().isEmpty()) {
@@ -52,23 +50,12 @@ class PolygonContainer implements Polygons {
}
}
- long[] ids = null;
- if (nativeMapView != null) {
- ids = nativeMapView.addPolygons(polygons);
- }
-
- long id = 0;
- for (int i = 0; i < polygons.size(); i++) {
+ long[] ids = nativeMapView.addPolygons(polygons);
+ for (int i = 0; i < ids.length; i++) {
polygon = polygons.get(i);
polygon.setMapboxMap(mapboxMap);
- if (ids != null) {
- id = ids[i];
- } else {
- // unit test
- id++;
- }
- polygon.setId(id);
- annotations.put(id, polygon);
+ polygon.setId(ids[i]);
+ annotations.put(ids[i], polygon);
}
}
return polygons;
@@ -76,11 +63,6 @@ class PolygonContainer implements Polygons {
@Override
public void update(Polygon polygon) {
- if (!isAddedToMap(polygon)) {
- Timber.w("Attempting to update non-added Polygon with value %s", polygon);
- return;
- }
-
nativeMapView.updatePolygon(polygon);
annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon);
}
@@ -97,8 +79,4 @@ class PolygonContainer implements Polygons {
}
return polygons;
}
-
- private boolean isAddedToMap(Annotation annotation) {
- return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) != -1;
- }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
index 7483f1082b..303b25fb55 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
@@ -11,8 +11,6 @@ import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import java.util.ArrayList;
import java.util.List;
-import timber.log.Timber;
-
/**
* Encapsulates {@link Polyline}'s functionality.
*/
@@ -43,8 +41,7 @@ class PolylineContainer implements Polylines {
int count = polylineOptionsList.size();
Polyline polyline;
List<Polyline> polylines = new ArrayList<>(count);
-
- if (count > 0) {
+ if (nativeMapView != null && count > 0) {
for (PolylineOptions options : polylineOptionsList) {
polyline = options.getPolyline();
if (!polyline.getPoints().isEmpty()) {
@@ -52,25 +49,12 @@ class PolylineContainer implements Polylines {
}
}
- long[] ids = null;
- if (nativeMapView != null) {
- ids = nativeMapView.addPolylines(polylines);
- }
-
- long id = 0;
- Polyline p;
-
- for (int i = 0; i < polylines.size(); i++) {
- p = polylines.get(i);
- p.setMapboxMap(mapboxMap);
- if (ids != null) {
- id = ids[i];
- } else {
- // unit test
- id++;
- }
- p.setId(id);
- annotations.put(id, p);
+ long[] ids = nativeMapView.addPolylines(polylines);
+ for (int i = 0; i < ids.length; i++) {
+ Polyline polylineCreated = polylines.get(i);
+ polylineCreated.setMapboxMap(mapboxMap);
+ polylineCreated.setId(ids[i]);
+ annotations.put(ids[i], polylineCreated);
}
}
return polylines;
@@ -78,10 +62,6 @@ class PolylineContainer implements Polylines {
@Override
public void update(Polyline polyline) {
- if (!isAddedToMap(polyline)) {
- Timber.w("Attempting to update non-added Polyline with value %s", polyline);
- }
-
nativeMapView.updatePolyline(polyline);
annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline);
}
@@ -98,8 +78,4 @@ class PolylineContainer implements Polylines {
}
return polylines;
}
-
- private boolean isAddedToMap(Annotation annotation) {
- return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) != -1;
- }
}
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 ff466c436c..16c73b1ca5 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
@@ -6,6 +6,7 @@ import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
import com.mapbox.mapboxsdk.geometry.VisibleRegion;
/**
@@ -45,6 +46,20 @@ public class Projection {
}
/**
+ * Returns the spherical Mercator projected meters for a LatLng.
+ */
+ public ProjectedMeters getProjectedMetersForLatLng(LatLng latLng) {
+ return nativeMapView.projectedMetersForLatLng(latLng);
+ }
+
+ /**
+ * Returns the LatLng for a spherical Mercator projected meters.
+ */
+ public LatLng getLatLngForProjectedMeters(ProjectedMeters projectedMeters) {
+ return nativeMapView.latLngForProjectedMeters(projectedMeters);
+ }
+
+ /**
* <p>
* Returns the distance spanned by one pixel at the specified latitude and current zoom level.
* </p>
@@ -78,24 +93,23 @@ public class Projection {
* @return The projection of the viewing frustum in its current state.
*/
public VisibleRegion getVisibleRegion() {
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
-
- float left = contentPadding[0];
- float right = nativeMapView.getWidth() - contentPadding[2];
- float top = contentPadding[1];
- float bottom = nativeMapView.getHeight() - contentPadding[3];
+ float left = 0;
+ float right = nativeMapView.getWidth();
+ float top = 0;
+ float bottom = nativeMapView.getHeight();
LatLng topLeft = fromScreenLocation(new PointF(left, top));
LatLng topRight = fromScreenLocation(new PointF(right, top));
LatLng bottomRight = fromScreenLocation(new PointF(right, bottom));
LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom));
- builder.include(topLeft)
- .include(topRight)
- .include(bottomRight)
- .include(bottomLeft);
-
- return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, builder.build());
+ return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight,
+ LatLngBounds.from(
+ topRight.getLatitude(),
+ topRight.getLongitude(),
+ bottomLeft.getLatitude(),
+ bottomLeft.getLongitude())
+ );
}
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java
new file mode 100644
index 0000000000..6ded2f32fb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java
@@ -0,0 +1,38 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.graphics.RectF;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class ShapeAnnotationContainer implements ShapeAnnotations {
+
+ private final NativeMapView nativeMapView;
+ private final LongSparseArray<Annotation> annotations;
+
+ ShapeAnnotationContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
+ this.nativeMapView = nativeMapView;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public List<Annotation> obtainAllIn(RectF rectangle) {
+ RectF rect = nativeMapView.getDensityDependantRectangle(rectangle);
+ long[] annotationIds = nativeMapView.queryShapeAnnotations(rect);
+ return getAnnotationsFromIds(annotationIds);
+ }
+
+ private List<Annotation> getAnnotationsFromIds(long[] annotationIds) {
+ List<Annotation> shapeAnnotations = new ArrayList<>();
+ for (long annotationId : annotationIds) {
+ Annotation annotation = annotations.get(annotationId);
+ if (annotation != null) {
+ shapeAnnotations.add(annotation);
+ }
+ }
+ return shapeAnnotations;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java
new file mode 100644
index 0000000000..f9b2ca10db
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java
@@ -0,0 +1,13 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.graphics.RectF;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+
+import java.util.List;
+
+interface ShapeAnnotations {
+
+ List<Annotation> obtainAllIn(RectF rectF);
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
index 77fea1a14a..6c90cd95ec 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
@@ -76,6 +76,9 @@ public class SupportMapFragment extends Fragment {
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
map.onCreate(savedInstanceState);
+ if (onMapReadyCallback != null) {
+ map.getMapAsync(onMapReadyCallback);
+ }
}
/**
@@ -85,7 +88,6 @@ public class SupportMapFragment extends Fragment {
public void onStart() {
super.onStart();
map.onStart();
- map.getMapAsync(onMapReadyCallback);
}
/**
@@ -150,6 +152,10 @@ public class SupportMapFragment extends Fragment {
* @param onMapReadyCallback The callback to be invoked.
*/
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
- this.onMapReadyCallback = onMapReadyCallback;
+ if (map == null) {
+ this.onMapReadyCallback = onMapReadyCallback;
+ } else {
+ map.getMapAsync(onMapReadyCallback);
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
index 7dcd84de75..6eacbbaeaf 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
@@ -6,11 +6,11 @@ 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.location.LocationSource;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.services.android.telemetry.location.LocationEngine;
import com.mapbox.services.android.telemetry.location.LocationEngineListener;
@@ -20,7 +20,11 @@ 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;
@@ -29,6 +33,8 @@ public final class TrackingSettings {
private final CameraZoomInvalidator zoomInvalidator;
private LocationEngine locationSource;
private LocationEngineListener myLocationListener;
+ private boolean locationChangeAnimationEnabled = true;
+ private boolean isCustomLocationSource;
private boolean myLocationEnabled;
private boolean dismissLocationTrackingOnGesture = true;
@@ -46,7 +52,7 @@ public final class TrackingSettings {
}
void initialise(MapboxMapOptions options) {
- locationSource = LocationSource.getLocationEngine(myLocationView.getContext());
+ locationSource = Mapbox.getLocationEngine();
setMyLocationEnabled(options.getLocationEnabled());
}
@@ -56,11 +62,16 @@ public final class TrackingSettings {
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));
+ 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
}
@@ -74,6 +85,8 @@ public final class TrackingSettings {
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));
}
/**
@@ -91,6 +104,7 @@ public final class TrackingSettings {
*/
@UiThread
public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
+ myLocationView.setLocationChangeAnimationEnabled(isLocationChangeAnimationEnabled());
myLocationView.setMyLocationTrackingMode(myLocationTrackingMode);
if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
@@ -254,6 +268,26 @@ public final class TrackingSettings {
}
/**
+ * 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.
*
@@ -314,6 +348,10 @@ public final class TrackingSettings {
}
}
+ public boolean isCustomLocationSource() {
+ return isCustomLocationSource;
+ }
+
void setOnMyLocationTrackingModeChangeListener(MapboxMap.OnMyLocationTrackingModeChangeListener listener) {
this.onMyLocationTrackingModeChangeListener = listener;
}
@@ -332,16 +370,30 @@ public final class TrackingSettings {
}
void setMyLocationEnabled(boolean locationEnabled) {
+ setMyLocationEnabled(locationEnabled, isCustomLocationSource());
+ }
+
+ private void setMyLocationEnabled(boolean locationEnabled, boolean isCustomLocationSource) {
if (!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;
- myLocationView.setEnabled(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);
}
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 7f44e0de07..16c45ebea2 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
@@ -79,7 +79,7 @@ final class Transform implements MapView.OnMapChangedListener {
@Override
public void onMapChanged(@MapView.MapChange int change) {
- if (change == REGION_DID_CHANGE_ANIMATED && cameraCancelableCallback != null) {
+ if (change == REGION_DID_CHANGE_ANIMATED) {
updateCameraPosition(invalidateCameraPosition());
if (cameraCancelableCallback != null) {
cameraCancelableCallback.onFinish();
@@ -93,14 +93,11 @@ final class Transform implements MapView.OnMapChangedListener {
@UiThread
final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, MapboxMap.CancelableCallback callback) {
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
- if (!cameraPosition.equals(this.cameraPosition)) {
+ 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);
- if (callback != null) {
- callback.onFinish();
- }
cameraChangeDispatcher.onCameraIdle();
}
}
@@ -109,15 +106,15 @@ final class Transform implements MapView.OnMapChangedListener {
final void easeCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, boolean easingInterpolator,
final MapboxMap.CancelableCallback callback, boolean isDismissable) {
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
- if (!cameraPosition.equals(this.cameraPosition)) {
+ if (isValidCameraPosition(cameraPosition)) {
trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, isDismissable);
cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
if (callback != null) {
cameraCancelableCallback = callback;
- mapView.addOnMapChangedListener(this);
}
+ mapView.addOnMapChangedListener(this);
mapView.easeTo(cameraPosition.bearing, cameraPosition.target, durationMs, cameraPosition.tilt,
cameraPosition.zoom, easingInterpolator);
}
@@ -127,21 +124,24 @@ final class Transform implements MapView.OnMapChangedListener {
final void animateCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs,
final MapboxMap.CancelableCallback callback) {
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
- if (!cameraPosition.equals(this.cameraPosition)) {
+ if (isValidCameraPosition(cameraPosition)) {
trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false);
cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
if (callback != null) {
cameraCancelableCallback = callback;
- mapView.addOnMapChangedListener(this);
}
-
+ mapView.addOnMapChangedListener(this);
mapView.flyTo(cameraPosition.bearing, cameraPosition.target, durationMs, cameraPosition.tilt,
cameraPosition.zoom);
}
}
+ private boolean isValidCameraPosition(@Nullable CameraPosition cameraPosition) {
+ return cameraPosition != null && !cameraPosition.equals(this.cameraPosition);
+ }
+
@UiThread
@Nullable
CameraPosition invalidateCameraPosition() {
@@ -151,6 +151,10 @@ final class Transform implements MapView.OnMapChangedListener {
cameraChangeDispatcher.onCameraMove();
}
+ if (isComponentUpdateRequired(cameraPosition)) {
+ updateCameraPosition(cameraPosition);
+ }
+
this.cameraPosition = cameraPosition;
if (onCameraChangeListener != null) {
onCameraChangeListener.onCameraChange(this.cameraPosition);
@@ -159,6 +163,11 @@ final class Transform implements MapView.OnMapChangedListener {
return cameraPosition;
}
+ private boolean isComponentUpdateRequired(@NonNull CameraPosition cameraPosition) {
+ return this.cameraPosition != null && (this.cameraPosition.tilt != cameraPosition.tilt
+ || this.cameraPosition.bearing != cameraPosition.bearing);
+ }
+
void cancelTransitions() {
// notify user about cancel
cameraChangeDispatcher.onCameraMoveCanceled();
@@ -198,11 +207,29 @@ final class Transform implements MapView.OnMapChangedListener {
return cameraPosition.zoom;
}
+ double getRawZoom() {
+ 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);
+ } 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);
+ } else {
+ // we are not transforming, notify about being idle
+ cameraChangeDispatcher.onCameraIdle();
}
}
@@ -211,16 +238,18 @@ final class Transform implements MapView.OnMapChangedListener {
}
void setZoom(double zoom, @NonNull PointF focalPoint, long duration) {
- mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
- @Override
- public void onMapChanged(int change) {
- if (change == MapView.REGION_DID_CHANGE_ANIMATED) {
- mapView.removeOnMapChangedListener(this);
- cameraChangeDispatcher.onCameraIdle();
+ if (mapView != null) {
+ mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
+ @Override
+ public void onMapChanged(int change) {
+ if (change == MapView.REGION_DID_CHANGE_ANIMATED) {
+ cameraChangeDispatcher.onCameraIdle();
+ mapView.removeOnMapChangedListener(this);
+ }
}
- }
- });
- mapView.setZoom(zoom, focalPoint, duration);
+ });
+ mapView.setZoom(zoom, focalPoint, duration);
+ }
}
// Direction
@@ -315,7 +344,7 @@ final class Transform implements MapView.OnMapChangedListener {
mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
@Override
public void onMapChanged(int change) {
- if (change == MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED) {
+ if (change == MapView.REGION_DID_CHANGE_ANIMATED) {
mapView.removeOnMapChangedListener(this);
cameraChangeDispatcher.onCameraIdle();
}
@@ -331,7 +360,7 @@ final class Transform implements MapView.OnMapChangedListener {
void setMinZoom(double minZoom) {
if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) {
- Timber.e("Not setting minZoomPreference, value is in unsupported range: " + minZoom);
+ Timber.e("Not setting minZoomPreference, value is in unsupported range: %s", minZoom);
return;
}
mapView.setMinZoom(minZoom);
@@ -343,7 +372,7 @@ final class Transform implements MapView.OnMapChangedListener {
void setMaxZoom(double maxZoom) {
if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) {
- Timber.e("Not setting maxZoomPreference, value is in unsupported range: " + maxZoom);
+ Timber.e("Not setting maxZoomPreference, value is in unsupported range: %s", maxZoom);
return;
}
mapView.setMaxZoom(maxZoom);
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 5f7b6c571b..0843828554 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
@@ -3,11 +3,8 @@ package com.mapbox.mapboxsdk.maps;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.PointF;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.ColorInt;
@@ -16,7 +13,6 @@ import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.res.ResourcesCompat;
-import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -25,10 +21,9 @@ import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
+import com.mapbox.mapboxsdk.utils.BitmapUtils;
import com.mapbox.mapboxsdk.utils.ColorUtils;
-import java.io.ByteArrayOutputStream;
-
/**
* Settings for the user interface of a MapboxMap. To obtain this interface, call getUiSettings().
*/
@@ -37,8 +32,14 @@ public final class UiSettings {
private final FocalPointChangeListener focalPointChangeListener;
private final Projection projection;
private final CompassView compassView;
+ private final int[] compassMargins = new int[4];
+
private final ImageView attributionsView;
+ private final int[] attributionsMargins = new int[4];
+
private final View logoView;
+ private final int[] logoMargins = new int[4];
+
private float pixelRatio;
private boolean rotateGesturesEnabled = true;
@@ -165,13 +166,7 @@ public final class UiSettings {
outState.putInt(MapboxConstants.STATE_COMPASS_MARGIN_RIGHT, getCompassMarginRight());
outState.putBoolean(MapboxConstants.STATE_COMPASS_FADE_WHEN_FACING_NORTH, isCompassFadeWhenFacingNorth());
outState.putByteArray(MapboxConstants.STATE_COMPASS_IMAGE_BITMAP,
- convert(MapboxMapOptions.getBitmapFromDrawable(getCompassImage())));
- }
-
- private byte[] convert(Bitmap resource) {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- resource.compress(Bitmap.CompressFormat.PNG, 100, stream);
- return stream.toByteArray();
+ BitmapUtils.getByteArrayFromDrawable(getCompassImage()));
}
private void restoreCompass(Bundle savedInstanceState) {
@@ -182,12 +177,8 @@ public final class UiSettings {
savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_MARGIN_RIGHT),
savedInstanceState.getInt(MapboxConstants.STATE_COMPASS_MARGIN_BOTTOM));
setCompassFadeFacingNorth(savedInstanceState.getBoolean(MapboxConstants.STATE_COMPASS_FADE_WHEN_FACING_NORTH));
- setCompassImage(decode(savedInstanceState.getByteArray(MapboxConstants.STATE_COMPASS_IMAGE_BITMAP)));
- }
-
- private Drawable decode(byte[] bitmap) {
- Bitmap compass = BitmapFactory.decodeByteArray(bitmap, 0, bitmap.length);
- return new BitmapDrawable(compassView.getResources(), compass);
+ setCompassImage(BitmapUtils.getDrawableFromByteArray(
+ compassView.getContext(), savedInstanceState.getByteArray(MapboxConstants.STATE_COMPASS_IMAGE_BITMAP)));
}
private void initialiseLogo(MapboxMapOptions options, Resources resources) {
@@ -309,8 +300,7 @@ public final class UiSettings {
* </p>
* By default, the compass is in the top right corner.
*
- * @param gravity One of the values from {@link Gravity}.
- * @see Gravity
+ * @param gravity Android SDK Gravity.
*/
@UiThread
public void setCompassGravity(int gravity) {
@@ -370,7 +360,7 @@ public final class UiSettings {
*/
@UiThread
public void setCompassMargins(int left, int top, int right, int bottom) {
- setWidgetMargins(compassView, left, top, right, bottom);
+ setWidgetMargins(compassView, compassMargins, left, top, right, bottom);
}
/**
@@ -379,7 +369,7 @@ public final class UiSettings {
* @return The left margin in pixels
*/
public int getCompassMarginLeft() {
- return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).leftMargin;
+ return compassMargins[0];
}
/**
@@ -388,7 +378,7 @@ public final class UiSettings {
* @return The top margin in pixels
*/
public int getCompassMarginTop() {
- return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).topMargin;
+ return compassMargins[1];
}
/**
@@ -397,7 +387,7 @@ public final class UiSettings {
* @return The right margin in pixels
*/
public int getCompassMarginRight() {
- return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).rightMargin;
+ return compassMargins[2];
}
/**
@@ -406,7 +396,7 @@ public final class UiSettings {
* @return The bottom margin in pixels
*/
public int getCompassMarginBottom() {
- return ((FrameLayout.LayoutParams) compassView.getLayoutParams()).bottomMargin;
+ return compassMargins[3];
}
/**
@@ -423,7 +413,8 @@ public final class UiSettings {
return;
}
- compassView.update(cameraPosition.bearing);
+ double clockwiseBearing = -cameraPosition.bearing;
+ compassView.update(clockwiseBearing);
}
/**
@@ -454,8 +445,7 @@ public final class UiSettings {
* </p>
* By default, the logo is in the bottom left corner.
*
- * @param gravity One of the values from {@link Gravity}.
- * @see Gravity
+ * @param gravity Android SDK Gravity.
*/
public void setLogoGravity(int gravity) {
setWidgetGravity(logoView, gravity);
@@ -480,7 +470,7 @@ public final class UiSettings {
* @param bottom The bottom margin in pixels.
*/
public void setLogoMargins(int left, int top, int right, int bottom) {
- setWidgetMargins(logoView, left, top, right, bottom);
+ setWidgetMargins(logoView, logoMargins, left, top, right, bottom);
}
/**
@@ -489,7 +479,7 @@ public final class UiSettings {
* @return The left margin in pixels
*/
public int getLogoMarginLeft() {
- return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).leftMargin;
+ return logoMargins[0];
}
/**
@@ -498,7 +488,7 @@ public final class UiSettings {
* @return The top margin in pixels
*/
public int getLogoMarginTop() {
- return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).topMargin;
+ return logoMargins[1];
}
/**
@@ -507,7 +497,7 @@ public final class UiSettings {
* @return The right margin in pixels
*/
public int getLogoMarginRight() {
- return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).rightMargin;
+ return logoMargins[2];
}
/**
@@ -516,7 +506,7 @@ public final class UiSettings {
* @return The bottom margin in pixels
*/
public int getLogoMarginBottom() {
- return ((FrameLayout.LayoutParams) logoView.getLayoutParams()).bottomMargin;
+ return logoMargins[3];
}
/**
@@ -546,8 +536,7 @@ public final class UiSettings {
* </p>
* By default, the attribution is in the bottom left corner next to the Mapbox logo.
*
- * @param gravity One of the values from {@link Gravity}.
- * @see Gravity
+ * @param gravity Android SDK Gravity.
*/
public void setAttributionGravity(int gravity) {
setWidgetGravity(attributionsView, gravity);
@@ -571,7 +560,7 @@ public final class UiSettings {
* @param bottom The bottom margin in pixels.
*/
public void setAttributionMargins(int left, int top, int right, int bottom) {
- setWidgetMargins(attributionsView, left, top, right, bottom);
+ setWidgetMargins(attributionsView, attributionsMargins, left, top, right, bottom);
}
/**
@@ -598,7 +587,7 @@ public final class UiSettings {
* @return The left margin in pixels
*/
public int getAttributionMarginLeft() {
- return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).leftMargin;
+ return attributionsMargins[0];
}
/**
@@ -607,7 +596,7 @@ public final class UiSettings {
* @return The top margin in pixels
*/
public int getAttributionMarginTop() {
- return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).topMargin;
+ return attributionsMargins[1];
}
/**
@@ -616,7 +605,7 @@ public final class UiSettings {
* @return The right margin in pixels
*/
public int getAttributionMarginRight() {
- return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).rightMargin;
+ return attributionsMargins[2];
}
/**
@@ -625,7 +614,7 @@ public final class UiSettings {
* @return The bottom margin in pixels
*/
public int getAttributionMarginBottom() {
- return ((FrameLayout.LayoutParams) attributionsView.getLayoutParams()).bottomMargin;
+ return attributionsMargins[3];
}
/**
@@ -929,7 +918,14 @@ public final class UiSettings {
view.setLayoutParams(layoutParams);
}
- private void setWidgetMargins(@NonNull final View view, int left, int top, int right, int bottom) {
+ private void setWidgetMargins(@NonNull final View view, int[] initMargins, int left, int top, int right, int bottom) {
+ // keep state of initially set margins
+ initMargins[0] = left;
+ initMargins[1] = top;
+ initMargins[2] = right;
+ initMargins[3] = bottom;
+
+ // convert inital margins with padding
int[] contentPadding = projection.getContentPadding();
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams();
left += contentPadding[0];
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
new file mode 100644
index 0000000000..fcee5bd179
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java
@@ -0,0 +1,142 @@
+package com.mapbox.mapboxsdk.maps.renderer;
+
+import android.content.Context;
+import android.support.annotation.CallSuper;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.storage.FileSource;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * The {@link MapRenderer} encapsulates the GL thread.
+ * <p>
+ * Performs actions on the GL thread to manage the GL resources and
+ * render on the one end and acts as a scheduler to request work to
+ * be performed on the GL thread on the other.
+ */
+public abstract class MapRenderer implements MapRendererScheduler {
+
+ // Holds the pointer to the native peer after initialisation
+ private long nativePtr = 0;
+
+ private MapboxMap.OnFpsChangedListener onFpsChangedListener;
+
+ public MapRenderer(Context context, String localIdeographFontFamily) {
+
+ FileSource fileSource = FileSource.getInstance(context);
+ float pixelRatio = context.getResources().getDisplayMetrics().density;
+ String programCacheDir = context.getCacheDir().getAbsolutePath();
+ // Initialise native peer
+ nativeInitialize(this, fileSource, pixelRatio, programCacheDir, localIdeographFontFamily);
+ }
+
+ public void onStart() {
+ // Implement if needed
+ }
+
+ public void onPause() {
+ // Implement if needed
+ }
+
+ public void onResume() {
+ // Implement if needed
+ }
+
+ public void onStop() {
+ // Implement if needed
+ }
+
+ public void onDestroy() {
+ // Implement if needed
+ }
+
+ public void setOnFpsChangedListener(MapboxMap.OnFpsChangedListener listener) {
+ onFpsChangedListener = listener;
+ }
+
+ @CallSuper
+ protected void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ nativeOnSurfaceCreated();
+ }
+
+ @CallSuper
+ protected void onSurfaceChanged(GL10 gl, int width, int height) {
+ if (width < 0) {
+ throw new IllegalArgumentException("fbWidth cannot be negative.");
+ }
+
+ if (height < 0) {
+ throw new IllegalArgumentException("fbHeight cannot be negative.");
+ }
+
+ if (width > 65535) {
+ throw new IllegalArgumentException(
+ "fbWidth cannot be greater than 65535.");
+ }
+
+ if (height > 65535) {
+ throw new IllegalArgumentException(
+ "fbHeight cannot be greater than 65535.");
+ }
+
+ gl.glViewport(0, 0, width, height);
+ nativeOnSurfaceChanged(width, height);
+ }
+
+ @CallSuper
+ protected void onDrawFrame(GL10 gl) {
+ nativeRender();
+
+ if (onFpsChangedListener != null) {
+ updateFps();
+ }
+ }
+
+ /**
+ * May be called from any thread.
+ * <p>
+ * Called from the native peer to schedule work on the GL
+ * thread. Explicit override for easier to read jni code.
+ *
+ * @param runnable the runnable to execute
+ * @see MapRendererRunnable
+ */
+ @CallSuper
+ void queueEvent(MapRendererRunnable runnable) {
+ this.queueEvent((Runnable) runnable);
+ }
+
+ private native void nativeInitialize(MapRenderer self,
+ FileSource fileSource,
+ float pixelRatio,
+ String programCacheDir,
+ String localIdeographFontFamily);
+
+ @CallSuper
+ @Override
+ protected native void finalize() throws Throwable;
+
+ private native void nativeOnSurfaceCreated();
+
+ private native void nativeOnSurfaceChanged(int width, int height);
+
+ private native void nativeRender();
+
+ private long frames;
+ private long timeElapsed;
+
+ private void updateFps() {
+ frames++;
+ long currentTime = System.nanoTime();
+ double fps = 0;
+ if (currentTime - timeElapsed >= 1) {
+ fps = frames / ((currentTime - timeElapsed) / 1E9);
+ onFpsChangedListener.onFpsChanged(fps);
+ timeElapsed = currentTime;
+ frames = 0;
+ }
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java
new file mode 100644
index 0000000000..28246fe578
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java
@@ -0,0 +1,29 @@
+package com.mapbox.mapboxsdk.maps.renderer;
+
+/**
+ * Peer class for {@link Runnable}s to be scheduled on the {@link MapRenderer} thread.
+ * The actual work is performed in the native peer.
+ */
+class MapRendererRunnable implements Runnable {
+
+ // Holds the pointer to the native peer after initialisation
+ private final long nativePtr;
+
+ /**
+ * Constructed from the native peer constructor
+ *
+ * @param nativePtr the native peer's memory address
+ */
+ MapRendererRunnable(long nativePtr) {
+ this.nativePtr = nativePtr;
+ }
+
+ @Override
+ public native void run();
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+ private native void nativeInitialize();
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java
new file mode 100644
index 0000000000..7ad4f124d8
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java
@@ -0,0 +1,13 @@
+package com.mapbox.mapboxsdk.maps.renderer;
+
+/**
+ * Can be used to schedule work on the map renderer
+ * thread or request a render.
+ */
+public interface MapRendererScheduler {
+
+ void requestRender();
+
+ void queueEvent(Runnable runnable);
+
+}
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
new file mode 100644
index 0000000000..b6f467708f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java
@@ -0,0 +1,293 @@
+package com.mapbox.mapboxsdk.maps.renderer.egl;
+
+import android.opengl.GLSurfaceView;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.utils.Compare.compare;
+import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_MASK_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_BUFFER_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_COLOR_BUFFER_TYPE;
+import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
+import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
+import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
+import static javax.microedition.khronos.egl.EGL10.EGL_RGB_BUFFER;
+import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
+import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
+import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
+import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
+
+/**
+ * Selects the right EGLConfig needed for `mapbox-gl-native`
+ */
+public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
+
+ /**
+ * Requires API level 17
+ *
+ * @see android.opengl.EGL14.EGL_CONFORMANT;
+ */
+ @SuppressWarnings("JavadocReference")
+ private static final int EGL_CONFORMANT = 0x3042;
+
+ /**
+ * Requires API level 17
+ *
+ * @see android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
+ */
+ @SuppressWarnings("JavadocReference")
+ private static final int EGL_OPENGL_ES2_BIT = 0x0004;
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] configAttribs = getConfigAttributes();
+
+ // Determine number of possible configurations
+ int[] numConfigs = getNumberOfConfigurations(egl, display, configAttribs);
+ if (numConfigs[0] < 1) {
+ Timber.e("eglChooseConfig() returned no configs.");
+ throw new EGLConfigException("eglChooseConfig() failed");
+ }
+
+ // Get all possible configurations
+ EGLConfig[] possibleConfigurations = getPossibleConfigurations(egl, display, configAttribs, numConfigs);
+
+ // Choose best match
+ EGLConfig config = chooseBestMatchConfig(egl, display, possibleConfigurations);
+ if (config == null) {
+ Timber.e("No config chosen");
+ throw new EGLConfigException("No config chosen");
+ }
+
+ return config;
+ }
+
+ private int[] getNumberOfConfigurations(EGL10 egl, EGLDisplay display, int[] configAttributes) {
+ int[] numConfigs = new int[1];
+ if (!egl.eglChooseConfig(display, configAttributes, null, 0, numConfigs)) {
+ Timber.e("eglChooseConfig(NULL) returned error %d", egl.eglGetError());
+ throw new EGLConfigException("eglChooseConfig() failed");
+ }
+ return numConfigs;
+ }
+
+ private EGLConfig[] getPossibleConfigurations(EGL10 egl, EGLDisplay display,
+ int[] configAttributes, int[] numConfigs) {
+ EGLConfig[] configs = new EGLConfig[numConfigs[0]];
+ if (!egl.eglChooseConfig(display, configAttributes, configs, numConfigs[0], numConfigs)) {
+ Timber.e("eglChooseConfig() returned error %d", egl.eglGetError());
+ throw new EGLConfigException("eglChooseConfig() failed");
+ }
+ return configs;
+ }
+
+ // Quality
+ enum BufferFormat {
+ Format16Bit(3),
+ Format32BitNoAlpha(1),
+ Format32BitAlpha(2),
+ Format24Bit(0),
+ Unknown(4);
+
+ int value;
+
+ BufferFormat(int value) {
+ this.value = value;
+ }
+ }
+
+ enum DepthStencilFormat {
+ Format16Depth8Stencil(1),
+ Format24Depth8Stencil(0);
+
+ int value;
+
+ DepthStencilFormat(int value) {
+ this.value = value;
+ }
+ }
+
+ private EGLConfig chooseBestMatchConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
+ class Config implements Comparable<Config> {
+ private final BufferFormat bufferFormat;
+ private final DepthStencilFormat depthStencilFormat;
+ private final boolean isNotConformant;
+ private final boolean isCaveat;
+ private final int index;
+ private final EGLConfig config;
+
+ public Config(BufferFormat bufferFormat, DepthStencilFormat depthStencilFormat,
+ boolean isNotConformant, boolean isCaveat, int index, EGLConfig config) {
+ this.bufferFormat = bufferFormat;
+ this.depthStencilFormat = depthStencilFormat;
+ this.isNotConformant = isNotConformant;
+ this.isCaveat = isCaveat;
+ this.index = index;
+ this.config = config;
+ }
+
+
+ @Override
+ public int compareTo(@NonNull Config other) {
+ int i = compare(bufferFormat.value, other.bufferFormat.value);
+ if (i != 0) {
+ return i;
+ }
+
+ i = compare(depthStencilFormat.value, other.depthStencilFormat.value);
+ if (i != 0) {
+ return i;
+ }
+
+ i = compare(isNotConformant, other.isNotConformant);
+ if (i != 0) {
+ return i;
+ }
+
+ i = compare(isCaveat, other.isCaveat);
+ if (i != 0) {
+ return i;
+ }
+
+ i = compare(index, other.index);
+ if (i != 0) {
+ return i;
+ }
+
+ return 0;
+ }
+ }
+
+ List<Config> matches = new ArrayList<>();
+
+ int i = 0;
+ for (EGLConfig config : configs) {
+ i++;
+
+ int caveat = getConfigAttr(egl, display, config, EGL_CONFIG_CAVEAT);
+ int conformant = getConfigAttr(egl, display, config, EGL_CONFORMANT);
+ int bits = getConfigAttr(egl, display, config, EGL_BUFFER_SIZE);
+ int red = getConfigAttr(egl, display, config, EGL_RED_SIZE);
+ int green = getConfigAttr(egl, display, config, EGL_GREEN_SIZE);
+ int blue = getConfigAttr(egl, display, config, EGL_BLUE_SIZE);
+ int alpha = getConfigAttr(egl, display, config, EGL_ALPHA_SIZE);
+ int alphaMask = getConfigAttr(egl, display, config, EGL_ALPHA_MASK_SIZE);
+ int depth = getConfigAttr(egl, display, config, EGL_DEPTH_SIZE);
+ int stencil = getConfigAttr(egl, display, config, EGL_STENCIL_SIZE);
+ int sampleBuffers = getConfigAttr(egl, display, config, EGL_SAMPLE_BUFFERS);
+ int samples = getConfigAttr(egl, display, config, EGL_SAMPLES);
+
+ boolean configOk = (depth == 24) || (depth == 16);
+ configOk &= stencil == 8;
+ configOk &= sampleBuffers == 0;
+ configOk &= samples == 0;
+
+ // Filter our configs first for depth, stencil and anti-aliasing
+ if (configOk) {
+ // Work out the config's buffer format
+ BufferFormat bufferFormat;
+ if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) {
+ bufferFormat = BufferFormat.Format16Bit;
+ } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
+ bufferFormat = BufferFormat.Format32BitNoAlpha;
+ } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
+ bufferFormat = BufferFormat.Format32BitAlpha;
+ } else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
+ bufferFormat = BufferFormat.Format24Bit;
+ } else {
+ bufferFormat = BufferFormat.Unknown;
+ }
+
+ // Work out the config's depth stencil format
+ DepthStencilFormat depthStencilFormat;
+ if ((depth == 16) && (stencil == 8)) {
+ depthStencilFormat = DepthStencilFormat.Format16Depth8Stencil;
+ } else {
+ depthStencilFormat = DepthStencilFormat.Format24Depth8Stencil;
+ }
+
+ boolean isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
+ boolean isCaveat = caveat != EGL_NONE;
+
+ // Ignore formats we don't recognise
+ if (bufferFormat != BufferFormat.Unknown) {
+ matches.add(new Config(bufferFormat, depthStencilFormat, isNotConformant, isCaveat, i, config));
+ }
+ }
+
+ }
+
+ // Sort
+ Collections.sort(matches);
+
+ if (matches.size() == 0) {
+ throw new EGLConfigException("No matching configurations after filtering");
+ }
+
+ Config bestMatch = matches.get(0);
+
+ if (bestMatch.isCaveat) {
+ Timber.w("Chosen config has a caveat.");
+ }
+
+ if (bestMatch.isNotConformant) {
+ Timber.w("Chosen config is not conformant.");
+ }
+
+ return bestMatch.config;
+ }
+
+ private int getConfigAttr(EGL10 egl, EGLDisplay display, EGLConfig config, int attributeName) {
+ int[] attributevalue = new int[1];
+ if (!egl.eglGetConfigAttrib(display, config, attributeName, attributevalue)) {
+ Timber.e("eglGetConfigAttrib(%d) returned error %d", attributeName, egl.eglGetError());
+ throw new EGLConfigException("eglGetConfigAttrib() failed");
+ }
+ return attributevalue[0];
+ }
+
+
+ private int[] getConfigAttributes() {
+ boolean emulator = inEmulator();
+ Timber.i("In emulator: %s", emulator);
+
+ // Get all configs at least RGB 565 with 16 depth and 8 stencil
+ return new int[] {
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_BUFFER_SIZE, 16,
+ EGL_RED_SIZE, 5,
+ EGL_GREEN_SIZE, 6,
+ EGL_BLUE_SIZE, 5,
+ EGL_ALPHA_SIZE, 0,
+ EGL_DEPTH_SIZE, 16,
+ EGL_STENCIL_SIZE, 8,
+ (emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT,
+ (emulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ };
+ }
+
+ /**
+ * Detect if we are in emulator.
+ */
+ private boolean inEmulator() {
+ return System.getProperty("ro.kernel.qemu") != null;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigException.java
new file mode 100644
index 0000000000..d5a1c9951f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigException.java
@@ -0,0 +1,21 @@
+package com.mapbox.mapboxsdk.maps.renderer.egl;
+
+/**
+ * Used for EGL configuration exceptions
+ */
+public class EGLConfigException extends RuntimeException {
+ public EGLConfigException() {
+ }
+
+ public EGLConfigException(String message) {
+ super(message);
+ }
+
+ public EGLConfigException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EGLConfigException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/GLSurfaceViewMapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/GLSurfaceViewMapRenderer.java
new file mode 100644
index 0000000000..7bc56475c0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/GLSurfaceViewMapRenderer.java
@@ -0,0 +1,81 @@
+package com.mapbox.mapboxsdk.maps.renderer.glsurfaceview;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+
+import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
+import com.mapbox.mapboxsdk.maps.renderer.egl.EGLConfigChooser;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY;
+
+/**
+ * The {@link GLSurfaceViewMapRenderer} encapsulates the GL thread and
+ * {@link GLSurfaceView} specifics to render the map.
+ *
+ * @see MapRenderer
+ */
+public class GLSurfaceViewMapRenderer extends MapRenderer implements GLSurfaceView.Renderer {
+
+ private final GLSurfaceView glSurfaceView;
+
+ public GLSurfaceViewMapRenderer(Context context, GLSurfaceView glSurfaceView, String localIdeographFontFamily) {
+ super(context, localIdeographFontFamily);
+ this.glSurfaceView = glSurfaceView;
+ glSurfaceView.setEGLContextClientVersion(2);
+ glSurfaceView.setEGLConfigChooser(new EGLConfigChooser());
+ glSurfaceView.setRenderer(this);
+ glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);
+ }
+
+ @Override
+ public void onStop() {
+ glSurfaceView.onPause();
+ }
+
+ @Override
+ public void onStart() {
+ glSurfaceView.onResume();
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ super.onSurfaceCreated(gl, config);
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ super.onSurfaceChanged(gl, width, height);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ super.onDrawFrame(gl);
+ }
+
+ /**
+ * May be called from any thread.
+ * <p>
+ * Called from the renderer frontend to schedule a render.
+ */
+ @Override
+ public void requestRender() {
+ glSurfaceView.requestRender();
+ }
+
+ /**
+ * May be called from any thread.
+ * <p>
+ * Schedules work to be performed on the MapRenderer thread.
+ *
+ * @param runnable the runnable to execute
+ */
+ @Override
+ public void queueEvent(Runnable runnable) {
+ glSurfaceView.queueEvent(runnable);
+ }
+
+
+}
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
new file mode 100644
index 0000000000..dcc95217ff
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java
@@ -0,0 +1,98 @@
+package com.mapbox.mapboxsdk.maps.renderer.textureview;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.view.TextureView;
+
+import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * The {@link TextureViewMapRenderer} encapsulates the GL thread and
+ * {@link TextureView} specifics to render the map.
+ *
+ * @see MapRenderer
+ */
+public class TextureViewMapRenderer extends MapRenderer {
+ private TextureViewRenderThread renderThread;
+
+ /**
+ * Create a {@link MapRenderer} for the given {@link TextureView}
+ *
+ * @param context the current Context
+ * @param textureView the TextureView
+ */
+ public TextureViewMapRenderer(@NonNull Context context,
+ @NonNull TextureView textureView,
+ String localIdeographFontFamily) {
+ super(context, localIdeographFontFamily);
+ renderThread = new TextureViewRenderThread(textureView, this);
+ renderThread.start();
+ }
+
+ /**
+ * Overridden to provide package access
+ */
+ @Override
+ protected void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ super.onSurfaceCreated(gl, config);
+ }
+
+ /**
+ * Overridden to provide package access
+ */
+ @Override
+ protected void onSurfaceChanged(GL10 gl, int width, int height) {
+ super.onSurfaceChanged(gl, width, height);
+ }
+
+ /**
+ * Overridden to provide package access
+ */
+ @Override
+ protected void onDrawFrame(GL10 gl) {
+ super.onDrawFrame(gl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void requestRender() {
+ renderThread.requestRender();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void queueEvent(Runnable runnable) {
+ renderThread.queueEvent(runnable);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStop() {
+ renderThread.onPause();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStart() {
+ renderThread.onResume();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onDestroy() {
+ renderThread.onDestroy();
+ }
+}
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
new file mode 100644
index 0000000000..c34833e9ce
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
@@ -0,0 +1,459 @@
+package com.mapbox.mapboxsdk.maps.renderer.textureview;
+
+import android.graphics.SurfaceTexture;
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+import android.view.TextureView;
+
+import com.mapbox.mapboxsdk.maps.renderer.egl.EGLConfigChooser;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL10;
+
+import timber.log.Timber;
+
+/**
+ * The render thread is responsible for managing the communication between the
+ * ui thread and the render thread it creates. Also, the EGL and GL contexts
+ * are managed from here.
+ */
+class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextureListener {
+
+ private final TextureViewMapRenderer mapRenderer;
+ private final EGLHolder eglHolder;
+
+ // Lock used for synchronization
+ private final Object lock = new Object();
+
+ // Guarded by lock
+ private final ArrayList<Runnable> eventQueue = new ArrayList<>();
+ private SurfaceTexture surface;
+ private int width;
+ private int height;
+ private boolean requestRender;
+ private boolean sizeChanged;
+ private boolean paused;
+ private boolean destroyContext;
+ private boolean destroySurface;
+ private boolean shouldExit;
+ private boolean exited;
+
+ /**
+ * Create a render thread for the given TextureView / Maprenderer combination.
+ *
+ * @param textureView the TextureView
+ * @param mapRenderer the MapRenderer
+ */
+ @UiThread
+ TextureViewRenderThread(@NonNull TextureView textureView, @NonNull TextureViewMapRenderer mapRenderer) {
+ textureView.setSurfaceTextureListener(this);
+ this.mapRenderer = mapRenderer;
+ this.eglHolder = new EGLHolder(new WeakReference<>(textureView));
+ }
+
+ // SurfaceTextureListener methods
+
+ @UiThread
+ @Override
+ public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) {
+ synchronized (lock) {
+ this.surface = surface;
+ this.width = width;
+ this.height = height;
+ this.requestRender = true;
+ lock.notifyAll();
+ }
+ }
+
+ @Override
+ @UiThread
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, final int width, final int height) {
+ synchronized (lock) {
+ this.width = width;
+ this.height = height;
+ this.sizeChanged = true;
+ this.requestRender = true;
+ lock.notifyAll();
+ }
+ }
+
+ @Override
+ @UiThread
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ synchronized (lock) {
+ this.surface = null;
+ this.destroySurface = true;
+ this.requestRender = false;
+ lock.notifyAll();
+ }
+ return true;
+ }
+
+ @Override
+ @UiThread
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ // Ignored
+ }
+
+ // MapRenderer delegate methods
+
+ /**
+ * May be called from any thread
+ */
+ void requestRender() {
+ synchronized (lock) {
+ requestRender = true;
+ lock.notifyAll();
+ }
+ }
+
+ /**
+ * May be called from any thread
+ */
+ void queueEvent(Runnable runnable) {
+ if (runnable == null) {
+ throw new IllegalArgumentException("runnable must not be null");
+ }
+ synchronized (lock) {
+ eventQueue.add(runnable);
+ lock.notifyAll();
+ }
+ }
+
+
+ @UiThread
+ void onPause() {
+ synchronized (lock) {
+ this.paused = true;
+ lock.notifyAll();
+ }
+ }
+
+ @UiThread
+ void onResume() {
+ synchronized (lock) {
+ this.paused = false;
+ lock.notifyAll();
+ }
+ }
+
+
+ @UiThread
+ void onDestroy() {
+ synchronized (lock) {
+ this.shouldExit = true;
+ lock.notifyAll();
+
+ // Wait for the thread to exit
+ while (!this.exited) {
+ try {
+ lock.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ // Thread implementation
+
+ @Override
+ public void run() {
+ try {
+
+ while (true) {
+ Runnable event = null;
+ boolean initializeEGL = false;
+ boolean recreateSurface = false;
+ int w = -1;
+ int h = -1;
+
+ // Guarded block
+ synchronized (lock) {
+ while (true) {
+
+ if (shouldExit) {
+ return;
+ }
+
+ // If any events are scheduled, pop one for processing
+ if (!eventQueue.isEmpty()) {
+ event = eventQueue.remove(0);
+ break;
+ }
+
+ if (destroySurface) {
+ eglHolder.destroySurface();
+ destroySurface = false;
+ break;
+ }
+
+ if (destroyContext) {
+ eglHolder.destroyContext();
+ destroyContext = false;
+ break;
+ }
+
+ if (surface != null && !paused && requestRender) {
+
+ w = width;
+ h = height;
+
+ // Initialize EGL if needed
+ if (eglHolder.eglContext == EGL10.EGL_NO_CONTEXT) {
+ initializeEGL = true;
+ break;
+ }
+
+ // (re-)Initialize EGL Surface if needed
+ if (eglHolder.eglSurface == EGL10.EGL_NO_SURFACE) {
+ recreateSurface = true;
+ break;
+ }
+
+ // Check if the size has changed
+ if (sizeChanged) {
+ recreateSurface = true;
+ sizeChanged = false;
+ break;
+ }
+
+ // Reset the request render flag now, so we can catch new requests
+ // while rendering
+ requestRender = false;
+
+ // Break the guarded loop and continue to process
+ break;
+ }
+
+
+ // Wait until needed
+ lock.wait();
+
+ } // end guarded while loop
+
+ } // end guarded block
+
+ // Run event, if any
+ if (event != null) {
+ event.run();
+ continue;
+ }
+
+ GL10 gl = eglHolder.createGL();
+
+ // Initialize EGL
+ if (initializeEGL) {
+ eglHolder.prepare();
+ if (!eglHolder.createSurface()) {
+ synchronized (lock) {
+ // Cleanup the surface if one could not be created
+ // and wait for another to be ready.
+ destroySurface = true;
+ }
+ continue;
+ }
+ mapRenderer.onSurfaceCreated(gl, eglHolder.eglConfig);
+ mapRenderer.onSurfaceChanged(gl, w, h);
+ continue;
+ }
+
+ // If the surface size has changed inform the map renderer.
+ if (recreateSurface) {
+ eglHolder.createSurface();
+ mapRenderer.onSurfaceChanged(gl, w, h);
+ continue;
+ }
+
+ // Don't continue without a surface
+ if (eglHolder.eglSurface == EGL10.EGL_NO_SURFACE) {
+ continue;
+ }
+
+ // Time to render a frame
+ mapRenderer.onDrawFrame(gl);
+
+ // Swap and check the result
+ int swapError = eglHolder.swap();
+ switch (swapError) {
+ case EGL10.EGL_SUCCESS:
+ break;
+ case EGL11.EGL_CONTEXT_LOST:
+ Timber.w("Context lost. Waiting for re-aquire");
+ synchronized (lock) {
+ surface = null;
+ destroySurface = true;
+ destroyContext = true;
+ }
+ break;
+ default:
+ Timber.w("eglSwapBuffer error: %s. Waiting or new surface", swapError);
+ // Probably lost the surface. Clear the current one and
+ // wait for a new one to be set
+ synchronized (lock) {
+ surface = null;
+ destroySurface = true;
+ }
+ }
+
+ }
+
+ } catch (InterruptedException err) {
+ // To be expected
+ } finally {
+ // Cleanup
+ eglHolder.cleanup();
+
+ // Signal we're done
+ synchronized (lock) {
+ this.exited = true;
+ lock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Holds the EGL state and offers methods to mutate it.
+ */
+ private static class EGLHolder {
+ private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ private final WeakReference<TextureView> textureViewWeakRef;
+
+ private EGL10 egl;
+ private EGLConfig eglConfig;
+ private EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
+ private EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
+ private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
+
+ EGLHolder(WeakReference<TextureView> textureViewWeakRef) {
+ this.textureViewWeakRef = textureViewWeakRef;
+ }
+
+ void prepare() {
+ this.egl = (EGL10) EGLContext.getEGL();
+
+ // Only re-initialize display when needed
+ if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
+ this.eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed");
+ }
+
+ int[] version = new int[2];
+ if (!egl.eglInitialize(eglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed");
+ }
+ }
+
+ if (textureViewWeakRef == null) {
+ // No texture view present
+ eglConfig = null;
+ eglContext = EGL10.EGL_NO_CONTEXT;
+ } else if (eglContext == EGL10.EGL_NO_CONTEXT) {
+ eglConfig = new EGLConfigChooser().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);
+ }
+
+ if (eglContext == EGL10.EGL_NO_CONTEXT) {
+ throw new RuntimeException("createContext");
+ }
+ }
+
+ GL10 createGL() {
+ return (GL10) eglContext.getGL();
+ }
+
+ boolean createSurface() {
+ // The window size has changed, so we need to create a new surface.
+ destroySurface();
+
+ // Create an EGL surface we can render into.
+ TextureView view = textureViewWeakRef.get();
+ if (view != null) {
+ int[] surfaceAttribs = {EGL10.EGL_NONE};
+ eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, view.getSurfaceTexture(), surfaceAttribs);
+ } else {
+ eglSurface = EGL10.EGL_NO_SURFACE;
+ }
+
+ if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) {
+ int error = egl.eglGetError();
+ if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+ Timber.e("createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ }
+ return false;
+ }
+
+ return makeCurrent();
+ }
+
+ boolean makeCurrent() {
+ if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
+ // Could not make the context current, probably because the underlying
+ // SurfaceView surface has been destroyed.
+ Timber.w("eglMakeCurrent: %s", egl.eglGetError());
+ return false;
+ }
+
+ return true;
+ }
+
+ int swap() {
+ if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) {
+ return egl.eglGetError();
+ }
+ return EGL10.EGL_SUCCESS;
+ }
+
+ private void destroySurface() {
+ if (eglSurface == EGL10.EGL_NO_SURFACE) {
+ return;
+ }
+
+ if (!egl.eglDestroySurface(eglDisplay, eglSurface)) {
+ Timber.w("Could not destroy egl surface. Display %s, Surface %s", eglDisplay, eglSurface);
+ }
+
+ eglSurface = EGL10.EGL_NO_SURFACE;
+ }
+
+ private void destroyContext() {
+ if (eglContext == EGL10.EGL_NO_CONTEXT) {
+ return;
+ }
+
+ if (!egl.eglDestroyContext(eglDisplay, eglContext)) {
+ Timber.w("Could not destroy egl context. Display %s, Context %s", eglDisplay, eglContext);
+ }
+
+ eglContext = EGL10.EGL_NO_CONTEXT;
+ }
+
+ private void terminate() {
+ if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
+ return;
+ }
+
+ if (!egl.eglTerminate(eglDisplay)) {
+ Timber.w("Could not terminate egl. Display %s", eglDisplay);
+ }
+ eglDisplay = EGL10.EGL_NO_DISPLAY;
+ }
+
+ void cleanup() {
+ destroySurface();
+ destroyContext();
+ terminate();
+ }
+ }
+}
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 2b327409ae..1e604c9bef 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,23 +1,18 @@
package com.mapbox.mapboxsdk.maps.widgets;
import android.content.Context;
-import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
-import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
-import com.mapbox.mapboxsdk.maps.FocalPointChangeListener;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import java.lang.ref.WeakReference;
-
/**
* UI element overlaid on a map to show the map's bearing when it isn't true north (0.0). Tapping
* the compass resets the bearing to true north and hides the compass.
@@ -27,16 +22,17 @@ import java.lang.ref.WeakReference;
* use {@link com.mapbox.mapboxsdk.maps.UiSettings}.
* </p>
*/
-public final class CompassView extends AppCompatImageView implements Runnable, FocalPointChangeListener {
+public final class CompassView extends ImageView implements Runnable {
- private static final long TIME_WAIT_IDLE = 500;
+ public static final long TIME_WAIT_IDLE = 500;
+ public static final long TIME_MAP_NORTH_ANIMATION = 150;
private static final long TIME_FADE_ANIMATION = TIME_WAIT_IDLE;
- private static final long TIME_MAP_NORTH_ANIMATION = 150;
private float rotation = 0.0f;
private boolean fadeCompassViewFacingNorth = true;
private ViewPropertyAnimatorCompat fadeAnimator;
- private PointF focalPoint;
+ private MapboxMap.OnCompassAnimationListener compassAnimationListener;
+ private boolean isAnimating = false;
public CompassView(Context context) {
super(context);
@@ -62,9 +58,12 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
setLayoutParams(lp);
}
- // TODO refactor MapboxMap and replace with interface
- public void setMapboxMap(@NonNull MapboxMap mapboxMap) {
- setOnClickListener(new CompassClickListener(mapboxMap, this));
+ public void injectCompassAnimationListener(@NonNull MapboxMap.OnCompassAnimationListener compassAnimationListener) {
+ this.compassAnimationListener = compassAnimationListener;
+ }
+
+ public void isAnimating(boolean isAnimating) {
+ this.isAnimating = isAnimating;
}
private void resetAnimation() {
@@ -97,11 +96,6 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
}
}
- @Nullable
- PointF getFocalPoint() {
- return focalPoint;
- }
-
/**
* Updates the direction of the compass.
*
@@ -126,6 +120,7 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
setVisibility(View.VISIBLE);
}
+ notifyCompassAnimationListenerWhenAnimating();
setRotation(rotation);
}
@@ -157,7 +152,8 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
@Override
public void run() {
- if (isFacingNorth() && fadeCompassViewFacingNorth) {
+ if (isHidden()) {
+ compassAnimationListener.onCompassAnimationFinished();
resetAnimation();
setLayerType(View.LAYER_TYPE_HARDWARE, null);
fadeAnimator = ViewCompat.animate(CompassView.this).alpha(0.0f).setDuration(TIME_FADE_ANIMATION);
@@ -172,34 +168,9 @@ public final class CompassView extends AppCompatImageView implements Runnable, F
}
}
- @Override
- public void onFocalPointChanged(PointF pointF) {
- focalPoint = pointF;
- }
-
- static class CompassClickListener implements View.OnClickListener {
-
- private WeakReference<MapboxMap> mapboxMap;
- private WeakReference<CompassView> compassView;
-
- CompassClickListener(final MapboxMap mapboxMap, CompassView compassView) {
- this.mapboxMap = new WeakReference<>(mapboxMap);
- this.compassView = new WeakReference<>(compassView);
- }
-
- @Override
- public void onClick(View view) {
- final MapboxMap mapboxMap = this.mapboxMap.get();
- final CompassView compassView = this.compassView.get();
- if (mapboxMap != null && compassView != null) {
- PointF focalPoint = compassView.getFocalPoint();
- if (focalPoint != null) {
- mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION);
- } else {
- mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION);
- }
- compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION);
- }
+ private void notifyCompassAnimationListenerWhenAnimating() {
+ if (isAnimating) {
+ compassAnimationListener.onCompassAnimation();
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
index f5ef46a5d3..aa7934ec1e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
@@ -1,6 +1,7 @@
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;
@@ -26,6 +27,7 @@ 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;
@@ -40,11 +42,21 @@ 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;
@@ -58,16 +70,18 @@ public class MyLocationView extends View {
private LatLng latLng;
private Location location;
- private LocationEngine locationSource;
+ 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() {
@@ -147,6 +161,17 @@ public class MyLocationView extends View {
compassListener = new CompassListener(context);
}
+ @Deprecated
+ public void init(LocationSource 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;
@@ -175,20 +200,35 @@ public class MyLocationView extends View {
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) {
- if (foregroundDrawable != null) {
- foregroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
- }
- if (foregroundBearingDrawable != null) {
- foregroundBearingDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
- }
+ 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;
@@ -202,14 +242,24 @@ public class MyLocationView extends View {
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;
}
- backgroundDrawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ 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);
@@ -217,6 +267,11 @@ public class MyLocationView extends View {
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();
@@ -297,15 +352,25 @@ public class MyLocationView extends View {
}
// draw foreground
- if (myBearingTrackingMode == MyBearingTracking.NONE || !compassListener.isSensorAvailable()) {
+ if (myBearingTrackingMode == MyBearingTracking.NONE) {
if (foregroundDrawable != null) {
foregroundDrawable.draw(canvas);
}
} else if (foregroundBearingDrawable != null && foregroundBounds != null) {
- foregroundBearingDrawable.draw(canvas);
+ if (myBearingTrackingMode == MyBearingTracking.GPS || 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;
if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
@@ -314,6 +379,11 @@ public class MyLocationView extends View {
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) {
@@ -327,6 +397,11 @@ public class MyLocationView extends View {
}
}
+ /**
+ * 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);
@@ -334,6 +409,9 @@ public class MyLocationView extends View {
}
}
+ /**
+ * Called when the hosting activity is starting, for internal use only.
+ */
public void onStart() {
if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) {
compassListener.onResume();
@@ -343,6 +421,9 @@ public class MyLocationView extends View {
}
}
+ /**
+ * Called when the hosting activity is stopping, for internal use only.
+ */
public void onStop() {
compassListener.onPause();
toggleGps(false);
@@ -368,12 +449,15 @@ public class MyLocationView extends View {
}
if (userLocationListener != null) {
- locationSource.removeLocationEngineListener(userLocationListener);
- locationSource = null;
+ locationEngine.removeLocationEngineListener(userLocationListener);
+ locationEngine = null;
userLocationListener = null;
}
}
+ /**
+ * Update current locationstate.
+ */
public void update() {
if (isEnabled()) {
myLocationBehavior.invalidate();
@@ -388,13 +472,33 @@ public class MyLocationView extends View {
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);
+ 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();
@@ -403,6 +507,11 @@ public class MyLocationView extends View {
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) {
@@ -413,45 +522,59 @@ public class MyLocationView extends View {
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
+ * 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) {
- if (locationSource == null) {
- locationSource = LocationSource.getLocationEngine(this.getContext());
- }
-
+ private void toggleGps(boolean enableGps, boolean isCustomLocationEngine) {
if (enableGps) {
- // Set an initial location if one available
- Location lastLocation = locationSource.getLastLocation();
-
- if (lastLocation != null) {
- setLocation(lastLocation);
+ if (locationEngine == null) {
+ if (!isCustomLocationEngine) {
+ locationEngine = Mapbox.getLocationEngine();
+ } else {
+ return;
+ }
}
if (userLocationListener == null) {
- userLocationListener = new GpsLocationListener(this, locationSource);
+ userLocationListener = new GpsLocationListener(this, locationEngine);
}
- locationSource.addLocationEngineListener(userLocationListener);
- locationSource.activate();
+ locationEngine.addLocationEngineListener(userLocationListener);
+ locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
+ locationEngine.activate();
} else {
+ if (locationEngine == null) {
+ return;
+ }
// Disable location and user dot
location = null;
- locationSource.removeLocationUpdates();
- locationSource.removeLocationEngineListener(userLocationListener);
- locationSource.deactivate();
+ locationEngine.removeLocationEngineListener(userLocationListener);
+ locationEngine.removeLocationUpdates();
+ locationEngine.deactivate();
+ restoreLocationEngine();
}
-
- locationSource.setPriority(LocationEnginePriority.HIGH_ACCURACY);
}
+ /**
+ * 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;
@@ -460,8 +583,37 @@ public class MyLocationView extends View {
this.location = location;
myLocationBehavior.updateLatLng(location);
+
+ if (mapboxMap != null && myBearingTrackingMode == MyBearingTracking.GPS
+ && 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()) {
@@ -478,6 +630,11 @@ public class MyLocationView extends View {
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);
@@ -485,8 +642,7 @@ public class MyLocationView extends View {
if (location != null) {
if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
// center map directly
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(new LatLng(location)), 0, false /*linear interpolator*/,
- null, true);
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(location)));
} else {
// do not use interpolated location from tracking mode
latLng = null;
@@ -498,17 +654,32 @@ public class MyLocationView extends View {
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 */);
}
@@ -536,14 +707,29 @@ public class MyLocationView extends View {
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;
}
@@ -553,8 +739,41 @@ public class MyLocationView extends View {
contentPaddingY = (padding[1] - padding[3]) / 2;
}
- public void setLocationSource(LocationEngine locationSource) {
- this.locationSource = locationSource;
+ /**
+ * 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 {
@@ -567,13 +786,16 @@ public class MyLocationView extends View {
locationSource = new WeakReference<>(locationEngine);
}
+ @SuppressLint("MissingPermission")
@Override
public void onConnected() {
MyLocationView locationView = userLocationView.get();
- if (locationView != null) {
- LocationEngine locationEngine = locationSource.get();
- Location location = locationEngine.getLastLocation();
- locationView.setLocation(location);
+ LocationEngine locationEngine = locationSource.get();
+ if (locationView != null && locationEngine != null) {
+ Location lastKnownLocation = locationEngine.getLastLocation();
+ if (lastKnownLocation != null) {
+ locationView.setLocation(lastKnownLocation);
+ }
locationEngine.requestLocationUpdates();
}
}
@@ -597,9 +819,12 @@ public class MyLocationView extends View {
private final SensorManager sensorManager;
private Sensor rotationVectorSensor;
- float[] matrix = new float[9];
- float[] orientation = new float[3];
+ 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;
@@ -617,6 +842,10 @@ public class MyLocationView extends View {
}
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;
}
@@ -630,9 +859,8 @@ public class MyLocationView extends View {
}
if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
-
- // calculate the rotation matrix
- SensorManager.getRotationMatrixFromVector(matrix, event.values);
+ rotationVectorValue = getRotationVectorFromSensorEvent(event);
+ SensorManager.getRotationMatrixFromVector(matrix, rotationVectorValue);
SensorManager.getOrientation(matrix, orientation);
magneticHeading = (float) Math.toDegrees(SensorManager.getOrientation(matrix, orientation)[0]);
@@ -649,6 +877,28 @@ public class MyLocationView extends View {
}
}
+ /**
+ * 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);
@@ -701,6 +951,12 @@ public class MyLocationView extends View {
private abstract class MyLocationBehavior {
+ MyLocationBehavior() {
+ if (latLng != null) {
+ locationUpdateTimestamp = SystemClock.elapsedRealtime();
+ }
+ }
+
void updateLatLng(@NonNull Location newLocation) {
location = newLocation;
}
@@ -719,10 +975,11 @@ public class MyLocationView extends View {
accuracyAnimator.end();
}
- accuracyAnimator = ValueAnimator.ofFloat(accuracy, location.getAccuracy());
+ float newAccuracy = location.getAccuracy() >= accuracyThreshold ? location.getAccuracy() : 0f;
+ accuracyAnimator = ValueAnimator.ofFloat(accuracy, newAccuracy);
accuracyAnimator.setDuration(750);
accuracyAnimator.start();
- accuracy = location.getAccuracy();
+ accuracy = newAccuracy;
}
abstract void invalidate();
@@ -767,9 +1024,13 @@ public class MyLocationView extends View {
// accuracy
updateAccuracy(location);
- // ease to new camera position with a linear interpolator
- mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, null,
- true);
+ 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
@@ -811,7 +1072,11 @@ public class MyLocationView extends View {
}
locationChangeAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- locationChangeAnimator.setDuration(locationUpdateDuration);
+ if (locationChangeAnimationEnabled) {
+ locationChangeAnimator.setDuration(locationUpdateDuration);
+ } else {
+ locationChangeAnimator.setDuration(0);
+ }
locationChangeAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(this,
latLng, newLocation
));
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
index e9d823ebda..ec7c53e1d0 100644
--- 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
@@ -1,19 +1,26 @@
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.camera.CameraPosition;
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;
@@ -51,6 +58,7 @@ public class MyLocationViewSettings {
//
private int accuracyAlpha;
+ private float accuracyThreshold = 0f;
@ColorInt
private int accuracyTintColor;
@@ -77,6 +85,11 @@ public class MyLocationViewSettings {
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)) {
@@ -88,6 +101,57 @@ public class MyLocationViewSettings {
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));
}
/**
@@ -114,6 +178,7 @@ public class MyLocationViewSettings {
* <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
@@ -122,6 +187,7 @@ public class MyLocationViewSettings {
this.foregroundDrawable = foregroundDrawable;
this.foregroundBearingDrawable = foregroundBearingDrawable;
myLocationView.setForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
+ myLocationView.setForegroundDrawableTint(foregroundTintColor);
}
/**
@@ -148,7 +214,8 @@ public class MyLocationViewSettings {
* The color will tint both the foreground and the bearing foreground drawable.
* </p>
*
- * @param foregroundTintColor the color to tint the foreground drawable
+ * @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;
@@ -169,6 +236,7 @@ public class MyLocationViewSettings {
* <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
@@ -181,6 +249,7 @@ public class MyLocationViewSettings {
} else {
myLocationView.setShadowDrawable(backgroundDrawable);
}
+ myLocationView.setShadowDrawableTint(backgroundTintColor);
}
/**
@@ -195,7 +264,8 @@ public class MyLocationViewSettings {
/**
* Set the background tint color.
*
- * @param backgroundTintColor the color to tint the background
+ * @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;
@@ -230,6 +300,10 @@ public class MyLocationViewSettings {
*/
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);
@@ -282,6 +356,25 @@ public class MyLocationViewSettings {
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);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java
index 7be56fa694..817dcdb438 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.net;
+import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -21,10 +22,14 @@ import timber.log.Timber;
* Not public api.
*/
public class ConnectivityReceiver extends BroadcastReceiver {
+ @SuppressLint("StaticFieldLeak")
private static ConnectivityReceiver INSTANCE;
/**
- * Get or create the singleton instance
+ * Get a single instance of ConnectivityReceiver.
+ *
+ * @param context the context to extract the application context from
+ * @return single instance of ConnectivityReceiver
*/
public static synchronized ConnectivityReceiver instance(Context context) {
if (INSTANCE == null) {
@@ -74,12 +79,12 @@ public class ConnectivityReceiver extends BroadcastReceiver {
}
/**
- * @see BroadcastReceiver#onReceive(Context, Intent)
+ * {@inheritDoc}
*/
@Override
public void onReceive(Context context, Intent intent) {
boolean connected = isConnected(context);
- Timber.v("Connected: " + connected);
+ Timber.v("Connected: %s", connected);
// Loop over listeners
for (ConnectivityListener listener : listeners) {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
index ce498da8f5..6a2bf6b07b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.offline;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
@@ -40,6 +41,7 @@ public class OfflineManager {
private Handler handler;
// This object is implemented as a singleton
+ @SuppressLint("StaticFieldLeak")
private static OfflineManager instance;
// The application context
@@ -90,11 +92,11 @@ public class OfflineManager {
*/
private OfflineManager(Context context) {
this.context = context.getApplicationContext();
- this.fileSource = FileSource.getInstance(context);
+ this.fileSource = FileSource.getInstance(this.context);
initialize(fileSource);
// Delete any existing previous ambient cache database
- deleteAmbientDatabase(context);
+ deleteAmbientDatabase(this.context);
}
private void deleteAmbientDatabase(final Context context) {
@@ -107,15 +109,21 @@ public class OfflineManager {
File file = new File(path);
if (file.exists()) {
file.delete();
- Timber.d("Old ambient cache database deleted to save space: " + path);
+ Timber.d("Old ambient cache database deleted to save space: %s", path);
}
} catch (Exception exception) {
- Timber.e("Failed to delete old ambient cache database: ", exception);
+ Timber.e(exception, "Failed to delete old ambient cache database: ");
}
}
}).start();
}
+ /**
+ * Get the single instance of offline manager.
+ *
+ * @param context the context used to host the offline manager
+ * @return the single instance of offline manager
+ */
public static synchronized OfflineManager getInstance(Context context) {
if (instance == null) {
instance = new OfflineManager(context);
@@ -141,8 +149,8 @@ public class OfflineManager {
*
* @param callback the callback to be invoked
*/
- public void listOfflineRegions(@NonNull
- final ListOfflineRegionsCallback callback) {
+ public void listOfflineRegions(@NonNull final ListOfflineRegionsCallback callback) {
+ fileSource.activate();
listOfflineRegions(fileSource, new ListOfflineRegionsCallback() {
@Override
@@ -150,6 +158,7 @@ public class OfflineManager {
getHandler().post(new Runnable() {
@Override
public void run() {
+ fileSource.deactivate();
callback.onList(offlineRegions);
}
});
@@ -160,6 +169,7 @@ public class OfflineManager {
getHandler().post(new Runnable() {
@Override
public void run() {
+ fileSource.deactivate();
callback.onError(error);
}
});
@@ -230,10 +240,12 @@ public class OfflineManager {
return LatLngBounds.world().contains(definition.getBounds());
}
- /*
- * Changing or bypassing this limit without permission from Mapbox is prohibited
- * by the Mapbox Terms of Service.
- */
+ /**
+ * Changing or bypassing this limit without permission from Mapbox is prohibited
+ * by the Mapbox Terms of Service.
+ *
+ * @param limit the new tile count limit.
+ */
public native void setOfflineMapboxTileCountLimit(long limit);
private native void initialize(FileSource fileSource);
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 fae53c2086..1b9a156352 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
@@ -300,10 +300,19 @@ public class OfflineRegion {
/**
* Pause or resume downloading of regional resources.
+ * <p>
+ * After a download has been completed, you are required to reset the state of the region to STATE_INACTIVE.
+ * </p>
*
* @param state the download state
*/
public void setDownloadState(@DownloadState int state) {
+ if (state == STATE_ACTIVE) {
+ fileSource.activate();
+ } else {
+ fileSource.deactivate();
+ }
+
this.state = state;
setOfflineRegionDownloadState(state);
}
@@ -358,10 +367,10 @@ public class OfflineRegion {
*/
public void delete(@NonNull final OfflineRegionDeleteCallback callback) {
if (!isDeleted) {
+ isDeleted = true;
deleteOfflineRegion(new OfflineRegionDeleteCallback() {
@Override
public void onDelete() {
- isDeleted = true;
getHandler().post(new Runnable() {
@Override
public void run() {
@@ -376,6 +385,7 @@ public class OfflineRegion {
getHandler().post(new Runnable() {
@Override
public void run() {
+ isDeleted = false;
callback.onError(error);
}
});
@@ -394,6 +404,7 @@ public class OfflineRegion {
* After you call this method, you may not call any additional methods on this object.
* </p>
*
+ * @param bytes the metadata in bytes
* @param callback the callback to be invoked
*/
public void updateMetadata(@NonNull final byte[] bytes, @NonNull final OfflineRegionUpdateMetadataCallback callback) {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
index 9c3655fbec..0f4b81fc39 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java
@@ -7,6 +7,7 @@ package com.mapbox.mapboxsdk.offline;
* <p>
* Note that the total required size in bytes is not currently available. A
* future API release may provide an estimate of this number.
+ * </p>
*/
public class OfflineRegionStatus {
@@ -51,6 +52,7 @@ public class OfflineRegionStatus {
* Specifically, it is false during early phases of an offline download. Once
* style and tile sources have been downloaded, it is possible to calculate the
* precise number of required resources, at which point it is set to true.
+ * </p>
*/
private boolean requiredResourceCountIsPrecise = true;
@@ -73,37 +75,93 @@ public class OfflineRegionStatus {
}
/**
- * Is the region complete?
+ * Validates if the region download has completed
+ *
+ * @return true if download is complete, false if not
*/
public boolean isComplete() {
- return (completedResourceCount == requiredResourceCount);
+ return (completedResourceCount == requiredResourceCount) && downloadState == OfflineRegion.STATE_INACTIVE;
}
+ /**
+ * Returns the download state.
+ * <p>
+ * State is defined as
+ * </p>
+ * <ul>
+ * <li>{@link OfflineRegion#STATE_ACTIVE}</li>
+ * <li>{@link OfflineRegion#STATE_INACTIVE}</li>
+ * </ul>
+ *
+ * @return the download state.
+ */
@OfflineRegion.DownloadState
public int getDownloadState() {
return downloadState;
}
+ /**
+ * Get the number of resources (inclusive of tiles) that have been fully downloaded
+ * and are ready for offline access.
+ *
+ * @return the amount of resources that have finished downloading.
+ */
public long getCompletedResourceCount() {
return completedResourceCount;
}
+ /**
+ * The cumulative size, in bytes, of all resources (inclusive of tiles) that have
+ * been fully downloaded.
+ *
+ * @return the size of the resources that have finished downloading
+ */
public long getCompletedResourceSize() {
return completedResourceSize;
}
+ /**
+ * Get the number of tiles that have been fully downloaded and are ready for
+ * offline access.
+ *
+ * @return the completed tile count
+ */
public long getCompletedTileCount() {
return completedTileCount;
}
+ /**
+ * Get the cumulative size, in bytes, of all tiles that have been fully downloaded.
+ *
+ * @return the completed tile size
+ */
public long getCompletedTileSize() {
return completedTileSize;
}
+ /**
+ * Get the number of resources that are known to be required for this region. See the
+ * documentation for `requiredResourceCountIsPrecise` for an important caveat
+ * about this number.
+ *
+ * @return the amount of resources that are required
+ */
public long getRequiredResourceCount() {
return requiredResourceCount;
}
+ /**
+ * Returns when the value of requiredResourceCount is a precise
+ * count of the number of required resources, and false when it is merely a lower
+ * bound.
+ * <p>
+ * Specifically, it is false during early phases of an offline download. Once
+ * style and tile sources have been downloaded, it is possible to calculate the
+ * precise number of required resources, at which point it is set to true.
+ * </p>
+ *
+ * @return True if the required resource count is precise, false if not
+ */
public boolean isRequiredResourceCountPrecise() {
return requiredResourceCountIsPrecise;
}
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 f8ec0f3d39..2a32f0bdd6 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
@@ -1,5 +1,9 @@
package com.mapbox.mapboxsdk.offline;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
/**
@@ -13,7 +17,7 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
* <p>
* pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0.
*/
-public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefinition {
+public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefinition, Parcelable {
private String styleURL;
private LatLngBounds bounds;
@@ -22,7 +26,7 @@ public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefiniti
private float pixelRatio;
/**
- * Constructor
+ * Constructor to create an OfflineTilePyramidDefinition from parameters.
*
* @param styleURL the style
* @param bounds the bounds
@@ -40,6 +44,22 @@ public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefiniti
this.pixelRatio = pixelRatio;
}
+ /**
+ * Constructor to create an OfflineTilePyramidDefinition from a Parcel.
+ *
+ * @param parcel the parcel to create the OfflineTilePyramidDefinition from
+ */
+ public OfflineTilePyramidRegionDefinition(Parcel parcel) {
+ this.styleURL = parcel.readString();
+ this.bounds = new LatLngBounds.Builder()
+ .include(new LatLng(parcel.readDouble(), parcel.readDouble()))
+ .include(new LatLng(parcel.readDouble(), parcel.readDouble()))
+ .build();
+ this.minZoom = parcel.readDouble();
+ this.maxZoom = parcel.readDouble();
+ this.pixelRatio = parcel.readFloat();
+ }
+
/*
* Getters
*/
@@ -64,4 +84,34 @@ public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefiniti
return pixelRatio;
}
+ /*
+ * Parceable
+ */
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(styleURL);
+ dest.writeDouble(bounds.getLatNorth());
+ dest.writeDouble(bounds.getLonEast());
+ dest.writeDouble(bounds.getLatSouth());
+ dest.writeDouble(bounds.getLonWest());
+ dest.writeDouble(minZoom);
+ dest.writeDouble(maxZoom);
+ dest.writeFloat(pixelRatio);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ public OfflineTilePyramidRegionDefinition createFromParcel(Parcel in) {
+ return new OfflineTilePyramidRegionDefinition(in);
+ }
+
+ public OfflineTilePyramidRegionDefinition[] newArray(int size) {
+ return new OfflineTilePyramidRegionDefinition[size];
+ }
+ };
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnaphotUtil.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnaphotUtil.java
new file mode 100644
index 0000000000..da86dc51fb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnaphotUtil.java
@@ -0,0 +1,29 @@
+package com.mapbox.mapboxsdk.snapshotter;
+
+import android.graphics.BitmapFactory;
+
+class MapSnaphotUtil {
+
+ static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth) {
+
+ final int halfHeight = height / 2;
+ final int halfWidth = width / 2;
+
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
+ // height and width larger than the requested height and width.
+ while ((halfHeight / inSampleSize) >= reqHeight
+ && (halfWidth / inSampleSize) >= reqWidth) {
+ inSampleSize *= 2;
+ }
+ }
+ return inSampleSize;
+ }
+
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java
new file mode 100644
index 0000000000..eb4f94c428
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java
@@ -0,0 +1,63 @@
+package com.mapbox.mapboxsdk.snapshotter;
+
+import android.graphics.Bitmap;
+import android.graphics.PointF;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+/**
+ * A completed snapshot.
+ *
+ * @see MapSnapshotter
+ */
+public class MapSnapshot {
+
+ private long nativePtr = 0;
+ private Bitmap bitmap;
+ private String[] attributions;
+ private boolean showLogo;
+
+ /**
+ * Created from native side
+ */
+ private MapSnapshot(long nativePtr, Bitmap bitmap, String[] attributions, boolean showLogo) {
+ this.nativePtr = nativePtr;
+ this.bitmap = bitmap;
+ this.attributions = attributions;
+ this.showLogo = showLogo;
+ }
+
+ /**
+ * @return the bitmap
+ */
+ public Bitmap getBitmap() {
+ return bitmap;
+ }
+
+ /**
+ * Calculate the point in pixels on the Image from geographical coordinates.
+ *
+ * @param latLng the geographical coordinates
+ * @return the point on the image
+ */
+ public native PointF pixelForLatLng(LatLng latLng);
+
+ /**
+ * @return The attributions for the sources of this snapshot.
+ */
+ protected String[] getAttributions() {
+ return attributions;
+ }
+
+ /**
+ * @return Flag indicating to show the Mapbox logo.
+ */
+ boolean isShowLogo() {
+ return showLogo;
+ }
+
+ // Unused, needed for peer binding
+ private native void initialize();
+
+ protected native void finalize();
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
new file mode 100644
index 0000000000..5deedc3e63
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
@@ -0,0 +1,367 @@
+package com.mapbox.mapboxsdk.snapshotter;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.util.DisplayMetrics;
+
+import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.storage.FileSource;
+
+/**
+ * The map snapshotter creates a bitmap of the map, rendered
+ * off the UI thread. The snapshotter itself must be used on
+ * the UI thread (for access to the main looper)
+ */
+@UiThread
+public class MapSnapshotter {
+
+ /**
+ * Get notified on snapshot completion.
+ *
+ * @see MapSnapshotter#start(SnapshotReadyCallback, ErrorHandler)
+ */
+ public interface SnapshotReadyCallback {
+
+ /**
+ * Called when the snapshot is complete.
+ *
+ * @param snapshot the snapshot
+ */
+ void onSnapshotReady(MapSnapshot snapshot);
+
+ }
+
+ /**
+ * Can be used to get notified of errors
+ * in snapshot generation
+ *
+ * @see MapSnapshotter#start(SnapshotReadyCallback, ErrorHandler)
+ */
+ public interface ErrorHandler {
+
+ /**
+ * Called on error. Snapshotting will not
+ * continue
+ *
+ * @param error the error message
+ */
+ void onError(String error);
+ }
+
+ private static final int LOGO_MARGIN_DP = 4;
+
+ // Holds the pointer to JNI NativeMapView
+ private long nativePtr = 0;
+
+ private final Context context;
+ private SnapshotReadyCallback callback;
+ private ErrorHandler errorHandler;
+
+ /**
+ * MapSnapshotter options
+ */
+ public static class Options {
+ private int pixelRatio = 1;
+ private int width;
+ private int height;
+ private String styleUrl = Style.MAPBOX_STREETS;
+ private LatLngBounds region;
+ private CameraPosition cameraPosition;
+ private boolean showLogo = true;
+
+ /**
+ * @param width the width of the image
+ * @param height the height of the image
+ */
+ public Options(int width, int height) {
+ this.width = width;
+ this.height = height;
+ }
+
+ /**
+ * @param url The style URL to use
+ * @return the mutated {@link Options}
+ */
+ public Options withStyle(String url) {
+ this.styleUrl = url;
+ return this;
+ }
+
+ /**
+ * @param region the region to show in the snapshot.
+ * This is applied after the camera position
+ * @return the mutated {@link Options}
+ */
+ public Options withRegion(LatLngBounds region) {
+ this.region = region;
+ return this;
+ }
+
+ /**
+ * @param pixelRatio the pixel ratio to use (default: 1)
+ * @return the mutated {@link Options}
+ */
+ public Options withPixelRatio(int pixelRatio) {
+ this.pixelRatio = pixelRatio;
+ return this;
+ }
+
+ /**
+ * @param cameraPosition The camera position to use,
+ * the {@link CameraPosition#target} is overridden
+ * by region if set in conjunction.
+ * @return the mutated {@link Options}
+ */
+ public Options withCameraPosition(CameraPosition cameraPosition) {
+ this.cameraPosition = cameraPosition;
+ return this;
+ }
+
+ /**
+ * @param showLogo The flag indicating to show the Mapbox logo.
+ * @return the mutated {@link Options}
+ */
+ public Options withLogo(boolean showLogo) {
+ this.showLogo = showLogo;
+ return this;
+ }
+
+ /**
+ * @return the width of the image
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * @return the height of the image
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * @return the pixel ratio
+ */
+ public int getPixelRatio() {
+ return pixelRatio;
+ }
+
+ /**
+ * @return the region
+ */
+ @Nullable
+ public LatLngBounds getRegion() {
+ return region;
+ }
+
+ /**
+ * @return the style url
+ */
+ public String getStyleUrl() {
+ return styleUrl;
+ }
+
+ /**
+ * @return the camera position
+ */
+ @Nullable
+ public CameraPosition getCameraPosition() {
+ return cameraPosition;
+ }
+ }
+
+ /**
+ * Creates the Map snapshotter, but doesn't start rendering or
+ * loading yet.
+ *
+ * @param context the Context that is or contains the Application context
+ * @param options the options to use for the snapshot
+ */
+ public MapSnapshotter(@NonNull Context context, @NonNull Options options) {
+ this.context = context.getApplicationContext();
+ FileSource fileSource = FileSource.getInstance(context);
+ String programCacheDir = context.getCacheDir().getAbsolutePath();
+
+ nativeInitialize(this, fileSource, options.pixelRatio, options.width,
+ options.height, options.styleUrl, options.region, options.cameraPosition,
+ options.showLogo, programCacheDir);
+ }
+
+ /**
+ * Starts loading and rendering the snapshot. The callback will be fired
+ * on the calling thread.
+ *
+ * @param callback the callback to use when the snapshot is ready
+ */
+ public void start(@NonNull SnapshotReadyCallback callback) {
+ this.start(callback, null);
+ }
+
+ /**
+ * Starts loading and rendering the snapshot. The callbacks will be fired
+ * on the calling thread.
+ *
+ * @param callback the callback to use when the snapshot is ready
+ * @param errorHandler the error handler to use on snapshot errors
+ */
+ public void start(@NonNull SnapshotReadyCallback callback, ErrorHandler errorHandler) {
+ if (this.callback != null) {
+ throw new IllegalStateException("Snapshotter was already started");
+ }
+
+ this.callback = callback;
+ this.errorHandler = errorHandler;
+ nativeStart();
+ }
+
+ /**
+ * Updates the snapshotter with a new size
+ *
+ * @param width the width
+ * @param height the height
+ */
+ public native void setSize(int width, int height);
+
+ /**
+ * Updates the snapshotter with a new {@link CameraPosition}
+ *
+ * @param cameraPosition the camera position
+ */
+ public native void setCameraPosition(CameraPosition cameraPosition);
+
+ /**
+ * Updates the snapshotter with a new {@link LatLngBounds}
+ *
+ * @param region the region
+ */
+ public native void setRegion(LatLngBounds region);
+
+ /**
+ * Updates the snapshotter with a new style url
+ *
+ * @param styleUrl the style url
+ */
+ public native void setStyleUrl(String styleUrl);
+
+
+ /**
+ * Must be called in on the thread
+ * the object was created on.
+ */
+ public void cancel() {
+ reset();
+ nativeCancel();
+ }
+
+ /**
+ * Draw an overlay on the map snapshot.
+ *
+ * @param mapSnapshot the map snapshot to draw the overlay on
+ */
+ protected void addOverlay(MapSnapshot mapSnapshot) {
+ Bitmap original = mapSnapshot.getBitmap();
+ Canvas canvas = new Canvas(original);
+ addLogo(canvas, original);
+ }
+
+ /**
+ * Draw a logo on the canvas created from the map snapshot.
+ *
+ * @param canvas the canvas to draw the bitmap on
+ * @param original the map snapshot image
+ */
+ private void addLogo(Canvas canvas, Bitmap original) {
+ DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ float margin = displayMetrics.density * LOGO_MARGIN_DP;
+ Bitmap logo = createScaledLogo(original);
+ canvas.drawBitmap(logo, margin, original.getHeight() - logo.getHeight() - margin, null);
+ }
+
+ /**
+ * Create a scaled logo for a map snapshot.
+ *
+ * @param snapshot the map snapshot where the logo should be placed on
+ * @return the scaled bitmap logo
+ */
+ private Bitmap createScaledLogo(Bitmap snapshot) {
+ Bitmap logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, null);
+ float scale = calculateLogoScale(snapshot, logo);
+ Matrix matrix = new Matrix();
+ matrix.postScale(scale, scale);
+ return Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
+ }
+
+ /**
+ * Calculates the scale of the logo, only allow downscaling.
+ *
+ * @param snapshot the bitmap of the map snapshot
+ * @param logo the bitmap of the mapbox logo
+ * @return the scale value
+ */
+ private float calculateLogoScale(Bitmap snapshot, Bitmap logo) {
+ DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ float widthRatio = displayMetrics.widthPixels / snapshot.getWidth();
+ float heightRatio = displayMetrics.heightPixels / snapshot.getHeight();
+ float prefWidth = logo.getWidth() / widthRatio;
+ float prefHeight = logo.getHeight() / heightRatio;
+ float calculatedScale = Math.min(prefWidth / logo.getWidth(), prefHeight / logo.getHeight()) * 2;
+ return calculatedScale < 1 ? calculatedScale : 1.0f;
+ }
+
+ /**
+ * Called by JNI peer when snapshot is ready.
+ * Always called on the origin (main) thread.
+ *
+ * @param snapshot the generated snapshot
+ */
+ protected void onSnapshotReady(MapSnapshot snapshot) {
+ if (callback != null) {
+ if (snapshot.isShowLogo()) {
+ addOverlay(snapshot);
+ }
+ callback.onSnapshotReady(snapshot);
+ reset();
+ }
+ }
+
+ /**
+ * Called by JNI peer when snapshot has failed.
+ * Always called on the origin (main) thread.
+ *
+ * @param reason the exception string
+ */
+ protected void onSnapshotFailed(String reason) {
+ if (errorHandler != null) {
+ errorHandler.onError(reason);
+ reset();
+ }
+ }
+
+ protected void reset() {
+ callback = null;
+ errorHandler = null;
+ }
+
+ protected native void nativeInitialize(MapSnapshotter mapSnapshotter,
+ FileSource fileSource, float pixelRatio,
+ int width, int height, String styleUrl,
+ LatLngBounds region, CameraPosition position,
+ boolean showLogo, String programCacheDir);
+
+ protected native void nativeStart();
+
+ protected native void nativeCancel();
+
+ @Override
+ protected native void finalize() throws Throwable;
+}
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 06676d76a1..f0cb8d973a 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
@@ -3,13 +3,11 @@ package com.mapbox.mapboxsdk.storage;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
import android.os.Environment;
import android.support.annotation.NonNull;
-import android.content.res.AssetManager;
-
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
-
import timber.log.Timber;
/**
@@ -28,8 +26,9 @@ public class FileSource {
/**
* Called whenever a URL needs to be transformed.
*
- * @param kind The kind of URL to be transformed.
- * @return A URL that will now be downloaded.
+ * @param kind the kind of URL to be transformed.
+ * @param url the URL to be transformed
+ * @return a URL that will now be downloaded.
*/
String onURL(@Resource.Kind int kind, String url);
@@ -38,6 +37,12 @@ public class FileSource {
// File source instance is kept alive after initialization
private static FileSource INSTANCE;
+ /**
+ * Get the single instance of FileSource.
+ *
+ * @param context the context to derive the cache path from
+ * @return the single instance of FileSource
+ */
public static synchronized FileSource getInstance(Context context) {
if (INSTANCE == null) {
String cachePath = getCachePath(context);
@@ -47,6 +52,12 @@ public class FileSource {
return INSTANCE;
}
+ /**
+ * Get the cache path for a context.
+ *
+ * @param context the context to derive the cache path from
+ * @return the cache path
+ */
public static String getCachePath(Context context) {
// Default value
boolean setStorageExternal = MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL;
@@ -59,9 +70,9 @@ public class FileSource {
MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL,
MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL);
} catch (PackageManager.NameNotFoundException exception) {
- Timber.e("Failed to read the package metadata: ", exception);
+ Timber.e(exception, "Failed to read the package metadata: ");
} catch (Exception exception) {
- Timber.e("Failed to read the storage key: ", exception);
+ Timber.e(exception, "Failed to read the storage key: ");
}
String cachePath = null;
@@ -70,7 +81,7 @@ public class FileSource {
// Try getting the external storage path
cachePath = context.getExternalFilesDir(null).getAbsolutePath();
} catch (NullPointerException exception) {
- Timber.e("Failed to obtain the external storage path: ", exception);
+ Timber.e(exception, "Failed to obtain the external storage path: ");
}
}
@@ -111,6 +122,10 @@ public class FileSource {
initialize(Mapbox.getAccessToken(), cachePath, assetManager);
}
+ public native void activate();
+
+ public native void deactivate();
+
public native void setAccessToken(@NonNull String accessToken);
public native String getAccessToken();
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..4d09fcaac6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
@@ -0,0 +1,1761 @@
+package com.mapbox.mapboxsdk.style.expressions;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.Size;
+
+import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The value for any layout property, paint property, or filter may be specified as an expression.
+ * An expression defines a formula for computing the value of the property using the operators described below.
+ * The set of expression operators provided by Mapbox GL includes:
+ * <p>
+ * <ul>
+ * <li>Element</li>
+ * <li>Mathematical operators for performing arithmetic and other operations on numeric values</li>
+ * <li>Logical operators for manipulating boolean values and making conditional decisions</li>
+ * <li>String operators for manipulating strings</li>
+ * <li>Data operators, providing access to the properties of source features</li>
+ * <li>Camera operators, providing access to the parameters defining the current map view</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Expressions are represented as JSON arrays.
+ * The first element of an expression array is a string naming the expression operator,
+ * e.g. "*"or "case". Subsequent elements (if any) are the arguments to the expression.
+ * Each argument is either a literal value (a string, number, boolean, or null), or another expression array.
+ * </p>
+ * <p>
+ * Data expression: a data expression is any expression that access feature data -- that is,
+ * any expression that uses one of the data operators:get,has,id,geometry-type, or properties.
+ * Data expressions allow a feature's properties to determine its appearance.
+ * They can be used to differentiate features within the same layer and to create data visualizations.
+ * </p>
+ * <p>
+ * Camera expression: a camera expression is any expression that uses the zoom operator.
+ * Such expressions allow the the appearance of a layer to change with the map's zoom level.
+ * Camera expressions can be used to create the appearance of depth and to control data density.
+ * </p>
+ * <p>
+ * Composition: a single expression may use a mix of data operators, camera operators, and other operators.
+ * Such composite expressions allows a layer's appearance to be determined by
+ * a combination of the zoom level and individual feature properties.
+ * </p>
+ *
+ * @param <T> the type of the expression
+ */
+public class Expression<T> {
+
+ private final String operator;
+ private final Expression[] arguments;
+
+ /**
+ * Creates an empty expression for expression literals
+ */
+ Expression() {
+ operator = null;
+ arguments = null;
+ }
+
+ /**
+ * Creates an expression from its operator and varargs expressions.
+ *
+ * @param operator the expression operator
+ * @param arguments expressions input
+ */
+ public Expression(@NonNull String operator, @Nullable Expression... arguments) {
+ this.operator = operator;
+ this.arguments = arguments;
+ }
+
+ /**
+ * Converts the expression to Object array representation.
+ * <p>
+ * The output will later be converted to a JSON Object array.
+ * </p>
+ *
+ * @return the converted object array expression
+ */
+ @NonNull
+ public Object[] toArray() {
+ List<Object> array = new ArrayList<>();
+ array.add(operator);
+ if (arguments != null) {
+ for (Expression argument : arguments) {
+ if (argument instanceof Expression.ExpressionLiteral) {
+ array.add(toValue((ExpressionLiteral) argument));
+ } else {
+ array.add(argument.toArray());
+ }
+ }
+ }
+ return array.toArray();
+ }
+
+ /**
+ * Converts the expression value to an Object.
+ *
+ * @param expressionValue the expression value to convert
+ * @return the converted object expression
+ */
+ private Object toValue(ExpressionLiteral expressionValue) {
+ Object value = expressionValue.toValue();
+ if (value instanceof Expression.Color) {
+ return ((Expression.Color) value).convertColor();
+ } else if (value instanceof Expression.ExpressionLiteral) {
+ return toValue((ExpressionLiteral) value);
+ } else if (value instanceof Expression) {
+ return ((Expression) value).toArray();
+ }
+ return value;
+ }
+
+ /**
+ * ExpressionLiteral wraps an object to be used as a literal in an expression.
+ * <p>
+ * ExpressionLiteral is created with {@link #literal(Number)}, {@link #literal(boolean)},
+ * {@link #literal(String)} and {@link #literal(Object)}.
+ * </p>
+ *
+ * @param <T>
+ */
+ private static class ExpressionLiteral<T> extends Expression<T> {
+
+ protected T object;
+
+ /**
+ * Create an ExpressionValue wrapper.
+ *
+ * @param object the object to be wrapped
+ */
+ ExpressionLiteral(@NonNull T object) {
+ this.object = object;
+ }
+
+ /**
+ * Get the wrapped object.
+ *
+ * @return the wrapped object
+ */
+ Object toValue() {
+ return object;
+ }
+ }
+
+ //
+ // Types
+ //
+
+ /**
+ * Expression interpolator type.
+ * <p>
+ * Is used for first parameter of {@link #interpolate(Expression, Expression, Stop...)}.
+ * </p>
+ */
+ private static class Interpolator {
+ }
+
+ /**
+ * Expression color type.
+ */
+ public static class Color {
+
+ private int color;
+
+ /**
+ * Creates a color color type from a color int.
+ *
+ * @param color the int color
+ */
+ public Color(@ColorInt int color) {
+ this.color = color;
+ }
+
+ /**
+ * Converts the int color to rgba(d, d, d, d) string representation
+ *
+ * @return
+ */
+ public String convertColor() {
+ return PropertyFactory.colorToRgbaString(color);
+ }
+ }
+
+ /**
+ * Expression array type.
+ */
+ public static class Array {
+ }
+
+ /**
+ * Expression stop type.
+ * <p>
+ * Can be used for {@link #stop(Object, Object)} as part of varargs parameter in
+ * {@link #step(Number, Expression, Stop...)} or {@link #interpolate(Expression, Expression, Stop...)}.
+ * </p>
+ */
+ public static class Stop {
+
+ private Object value;
+ private Object output;
+
+ public Stop(Object value, Object output) {
+ this.value = value;
+ this.output = output;
+ }
+ }
+
+ //
+ // Literals
+ //
+
+ /**
+ * Create a literal number expression.
+ *
+ * @param number the number
+ * @return the expression
+ */
+ public static Expression<Number> literal(@NonNull Number number) {
+ return new ExpressionLiteral<>(number);
+ }
+
+ /**
+ * Create a literal string expression.
+ *
+ * @param string the string
+ * @return the expression
+ */
+ public static Expression<String> literal(@NonNull String string) {
+ return new ExpressionLiteral<>(string);
+ }
+
+ /**
+ * Create a literal boolean expression.
+ *
+ * @param bool the boolean
+ * @return the expression
+ */
+ public static Expression<Boolean> literal(boolean bool) {
+ return new ExpressionLiteral<>(bool);
+ }
+
+ /**
+ * Create a literal object expression
+ *
+ * @param object the object
+ * @return the expression
+ */
+ public static Expression<Object> literal(@NonNull Object object) {
+ return new ExpressionLiteral<>(object);
+ }
+
+ //
+ // Color
+ //
+
+ /**
+ * Expression literal utility method to convert a color int to an color expression
+ *
+ * @param color the int color
+ * @return the color expression
+ */
+ public static Expression<Color> color(@ColorInt int color) {
+ return new ExpressionLiteral<>(new Color(color));
+ }
+
+ /**
+ * Creates a color value from red, green, and blue components, which must range between 0 and 255,
+ * and an alpha component of 1.
+ * <p>
+ * If any component is out of range, the expression is an error.
+ * </p>
+ *
+ * @param red red color expression
+ * @param green green color expression
+ * @param blue blue color expression
+ * @return expression
+ */
+ public static Expression<Color> rgb(@NonNull Expression<Number> red, @NonNull Expression<Number> green,
+ @NonNull Expression<Number> blue) {
+ return new Expression<>("rgb", red, green, blue);
+ }
+
+ /**
+ * Creates a color value from red, green, and blue components, which must range between 0 and 255,
+ * and an alpha component of 1.
+ * <p>
+ * If any component is out of range, the expression is an error.
+ * </p>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @return expression
+ */
+ public static Expression<Color> rgb(@NonNull Number red, @NonNull Number green, @NonNull Number blue) {
+ return rgb(literal(red), literal(green), literal(blue));
+ }
+
+ /**
+ * Creates a color value from red, green, blue components, which must range between 0 and 255,
+ * and an alpha component which must range between 0 and 1.
+ * <p>
+ * If any component is out of range, the expression is an error.
+ * </p>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @param alpha alpha color value
+ * @return expression
+ */
+ public static Expression<Color> rgba(@NonNull Expression<Number> red, @NonNull Expression<Number> green,
+ @NonNull Expression<Number> blue, @NonNull Expression<Number> alpha) {
+ return new Expression<>("rgba", red, green, blue, alpha);
+ }
+
+ /**
+ * Creates a color value from red, green, blue components, which must range between 0 and 255,
+ * and an alpha component which must range between 0 and 1.
+ * <p>
+ * If any component is out of range, the expression is an error.
+ * </p>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @param alpha alpha color value
+ * @return expression
+ */
+ public static Expression<Color> rgba(@NonNull Number red, @NonNull Number green, @NonNull Number blue, @NonNull Number alpha) {
+ return rgba(literal(red), literal(green), literal(blue), literal(alpha));
+ }
+
+ /**
+ * Returns a four-element array containing the input color's red, green, blue, and alpha components, in that order.
+ *
+ * @param expression an expression to convert to a color
+ * @return expression
+ */
+ public static Expression<Array> toRgba(@NonNull Expression<Color> expression) {
+ return new Expression<>("to-rgba", expression);
+ }
+
+ //
+ // Decision
+ //
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ * The inputs must be numbers, strings, or booleans, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> eq(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>("==", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ *
+ * @param compareOne the first boolean
+ * @param compareTwo the second boolean
+ * @return expression
+ */
+ public static Expression<Boolean> eq(boolean compareOne, boolean compareTwo) {
+ return eq(literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> eq(@NonNull String compareOne, @NonNull String compareTwo) {
+ return eq(literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> eq(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return eq(literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the input values are not equal, false otherwise.
+ * The inputs must be numbers, strings, or booleans, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> neq(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>("!=", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ *
+ * @param compareOne the first boolean
+ * @param compareTwo the second boolean
+ * @return expression
+ */
+ public static Expression<Boolean> neq(boolean compareOne, boolean compareTwo) {
+ return new Expression<>("!=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns `true` if the input values are not equal, `false` otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> neq(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>("!=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns `true` if the input values are not equal, `false` otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> neq(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>("!=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is strictly greater than the second, false otherwise.
+ * The inputs must be numbers or strings, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> gt(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>(">", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the first input is strictly greater than the second, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> gt(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>(">", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is strictly greater than the second, false otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> gt(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>(">", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is strictly less than the second, false otherwise.
+ * The inputs must be numbers or strings, and both of the same type.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> lt(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>("<", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the first input is strictly less than the second, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> lt(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>("<", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is strictly less than the second, false otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> lt(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>("<", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is greater than or equal to the second, false otherwise.
+ * The inputs must be numbers or strings, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> gte(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>(">=", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the first input is greater than or equal to the second, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> gte(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>(">=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is greater than or equal to the second, false otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> gte(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>(">=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is less than or equal to the second, false otherwise.
+ * The inputs must be numbers or strings, and both of the same type.
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ */
+ public static Expression<Boolean> lte(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression<>("<=", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the first input is less than or equal to the second, false otherwise.
+ *
+ * @param compareOne the first number
+ * @param compareTwo the second number
+ * @return expression
+ */
+ public static Expression<Boolean> lte(@NonNull Number compareOne, @NonNull Number compareTwo) {
+ return new Expression<>("<=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the first input is less than or equal to the second, false otherwise.
+ *
+ * @param compareOne the first string
+ * @param compareTwo the second string
+ * @return expression
+ */
+ public static Expression<Boolean> lte(@NonNull String compareOne, @NonNull String compareTwo) {
+ return new Expression<>("<=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns `true` if all the inputs are `true`, `false` otherwise.
+ * <p>
+ * The inputs are evaluated in order, and evaluation is short-circuiting:
+ * once an input expression evaluates to `false`,
+ * the result is `false` and no further input expressions are evaluated.
+ * </p>
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> all(@NonNull Expression<Boolean>... input) {
+ return new Expression<>("all", input);
+ }
+
+ /**
+ * Returns `true` if any of the inputs are `true`, `false` otherwise.
+ * <p>
+ * The inputs are evaluated in order, and evaluation is short-circuiting:
+ * once an input expression evaluates to `true`,
+ * the result is `true` and no further input expressions are evaluated.
+ * </p>
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> any(@NonNull Expression<Boolean>... input) {
+ return new Expression<>("any", input);
+ }
+
+ /**
+ * Logical negation. Returns `true` if the input is `false`, and `false` if the input is `true`.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> not(@NonNull Expression<Boolean> input) {
+ return new Expression<>("!", input);
+ }
+
+ /**
+ * Logical negation. Returns `true` if the input is `false`, and `false` if the input is `true`.
+ *
+ * @param input boolean input
+ * @return expression
+ */
+ public static Expression<Boolean> not(boolean input) {
+ return not(literal(input));
+ }
+
+ /**
+ * Selects the first output whose corresponding test condition evaluates to true.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression switchCase(@NonNull @Size(min = 1) Expression... input) {
+ return new Expression("case", input);
+ }
+
+ /**
+ * Selects the output whose label value matches the input value, or the fallback value if no match is found.
+ * The `input` can be any string or number expression.
+ * Each label can either be a single literal value or an array of values.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression match(@NonNull @Size(min = 2) Expression... input) {
+ return new Expression("match", input);
+ }
+
+ /**
+ * Selects the output whose label value matches the input value, or the fallback value if no match is found.
+ * The `input` can be any string or number expression.
+ * Each label can either be a single literal value or an array of values.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression match(@NonNull Expression input, @NonNull Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressions[i * 2] = literal(stops[i].value);
+ expressions[i * 2 + 1] = literal(stops[i].output);
+ }
+ return match(join(new Expression[] {input}, expressions));
+ }
+
+ /**
+ * Evaluates each expression in turn until the first non-null value is obtained, and returns that value.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression coalesce(@NonNull Expression... input) {
+ return new Expression("coalesce", input);
+ }
+
+ //
+ // FeatureData
+ //
+
+ /**
+ * Gets the feature properties object.
+ * <p>
+ * Note that in some cases, it may be more efficient to use {@link #get(Expression)}} instead.
+ * </p>
+ *
+ * @return expression
+ */
+ public static Expression<Object> properties() {
+ return new Expression<>("properties");
+ }
+
+ /**
+ * Gets the feature's geometry type: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon.
+ *
+ * @return expression
+ */
+ public static Expression<String> geometryType() {
+ return new Expression<>("geometry-type");
+ }
+
+ /**
+ * Gets the feature's id, if it has one.
+ *
+ * @return expression
+ */
+ public static Expression<Number> id() {
+ return new Expression<>("id");
+ }
+
+ //
+ // Heatmap
+ //
+
+ /**
+ * Gets the kernel density estimation of a pixel in a heatmap layer,
+ * which is a relative measure of how many data points are crowded around a particular pixel.
+ * Can only be used in the `heatmap-color` property.
+ *
+ * @return expression
+ */
+ public static Expression<Number> heatmapDensity() {
+ return new Expression<>("heatmap-density");
+ }
+
+ //
+ // Lookup
+ //
+
+ /**
+ * Retrieves an item from an array.
+ *
+ * @param number the index expression
+ * @param expression the array expression
+ * @return expression
+ */
+ public static Expression<Object> at(@NonNull Expression<Number> number, @NonNull Expression expression) {
+ return new Expression<>("at", number, expression);
+ }
+
+ /**
+ * Retrieves an item from an array.
+ *
+ * @param number the index expression
+ * @param expression the array expression
+ * @return expression
+ */
+ public static Expression<Object> at(@NonNull Number number, @NonNull Expression expression) {
+ return at(literal(number), expression);
+ }
+
+ /**
+ * Retrieves a property value from the current feature's properties,
+ * or from another object if a second argument is provided.
+ * Returns null if the requested property is missing.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression get(@NonNull Expression<String> input) {
+ return new Expression<>("get", input);
+ }
+
+ /**
+ * Retrieves a property value from the current feature's properties,
+ * or from another object if a second argument is provided.
+ * Returns null if the requested property is missing.
+ *
+ * @param input string input
+ * @return expression
+ */
+ public static Expression get(@NonNull String input) {
+ return get(literal(input));
+ }
+
+ /**
+ * Retrieves a property value from another object.
+ * Returns null if the requested property is missing.
+ *
+ * @param key a property value key
+ * @param object an expression object
+ * @return expression
+ */
+ public static Expression get(@NonNull Expression<String> key, @NonNull Expression<Object> object) {
+ return new Expression<>("get", key, object);
+ }
+
+ /**
+ * Retrieves a property value from another object.
+ * Returns null if the requested property is missing.
+ *
+ * @param key a property value key
+ * @param object an expression object
+ * @return expression
+ */
+ public static Expression get(@NonNull String key, @NonNull Expression<Object> object) {
+ return get(literal(key), object);
+ }
+
+ /**
+ * Tests for the presence of an property value in the current feature's properties.
+ *
+ * @param key the expression property value key
+ * @return expression
+ */
+ public static Expression<Boolean> has(@NonNull Expression<String> key) {
+ return new Expression<>("has", key);
+ }
+
+ /**
+ * Tests for the presence of an property value in the current feature's properties.
+ *
+ * @param key the property value key
+ * @return expression
+ */
+ public static Expression<Boolean> has(@NonNull String key) {
+ return has(literal(key));
+ }
+
+ /**
+ * Tests for the presence of an property value from another object.
+ *
+ * @param key the expression property value key
+ * @param object an expression object
+ * @return expression
+ */
+ public static Expression<Boolean> has(@NonNull Expression<String> key, @NonNull Expression<Object> object) {
+ return new Expression<>("has", key, object);
+ }
+
+ /**
+ * Tests for the presence of an property value from another object.
+ *
+ * @param key the property value key
+ * @param object an expression object
+ * @return expression
+ */
+ public static Expression<Boolean> has(@NonNull String key, @NonNull Expression<Object> object) {
+ return has(literal(key), object);
+ }
+
+ /**
+ * Gets the length of an array or string.
+ *
+ * @param expression an expression object or expression string
+ * @return expression
+ */
+ public static Expression<Number> length(@NonNull Expression<?> expression) {
+ return new Expression<>("length", expression);
+ }
+
+ /**
+ * Gets the length of an array or string.
+ *
+ * @param input a string
+ * @return expression
+ */
+ public static Expression<Number> length(@NonNull String input) {
+ return length(literal(input));
+ }
+
+ //
+ // Math
+ //
+
+ /**
+ * Returns mathematical constant ln(2).
+ *
+ * @return expression
+ */
+ public static Expression<Number> ln2() {
+ return new Expression<>("ln2");
+ }
+
+ /**
+ * Returns the mathematical constant pi.
+ *
+ * @return expression
+ */
+ public static Expression<Number> pi() {
+ return new Expression<>("pi");
+ }
+
+ /**
+ * Returns the mathematical constant e.
+ *
+ * @return expression
+ */
+ public static Expression<Number> e() {
+ return new Expression<>("e");
+ }
+
+ /**
+ * Returns the sum of the inputs.
+ *
+ * @param numbers the numbers to calculate the sum for
+ * @return expression
+ */
+ public static Expression<Number> sum(@Size(min = 2) Expression<Number>... numbers) {
+ return new Expression<>("+", numbers);
+ }
+
+ /**
+ * Returns the sum of the inputs.
+ *
+ * @param numbers the numbers to calculate the sum for
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> sum(@Size(min = 2) Number... numbers) {
+ Expression<Number>[] numberExpression = (Expression<Number>[]) new Expression<?>[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return sum(numberExpression);
+ }
+
+ /**
+ * Returns the product of the inputs.
+ *
+ * @param numbers the numbers to calculate the product for
+ * @return expression
+ */
+ public static Expression<Number> product(@Size(min = 2) Expression<Number>... numbers) {
+ return new Expression<>("*", numbers);
+ }
+
+ /**
+ * Returns the product of the inputs.
+ *
+ * @param numbers the numbers to calculate the product for
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> product(@Size(min = 2) Number... numbers) {
+ Expression<Number>[] numberExpression = (Expression<Number>[]) new Expression<?>[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return product(numberExpression);
+ }
+
+ /**
+ * Returns the result of subtracting a number from 0.
+ *
+ * @param number the number subtract from 0
+ * @return expression
+ */
+ public static Expression<Number> subtract(@NonNull Expression<Number> number) {
+ return new Expression<>("-", number);
+ }
+
+ /**
+ * Returns the result of subtracting a number from 0.
+ *
+ * @param number the number subtract from 0
+ * @return expression
+ */
+ public static Expression<Number> subtract(@NonNull Number number) {
+ return subtract(literal(number));
+ }
+
+ /**
+ * Returns the result of subtracting the second input from the first.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> subtract(@NonNull Expression<Number> first, @NonNull Expression<Number> second) {
+ return new Expression<>("-", first, second);
+ }
+
+ /**
+ * Returns the result of subtracting the second input from the first.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> subtract(@NonNull Number first, @NonNull Number second) {
+ return subtract(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the result of floating point division of the first input by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> division(@NonNull Expression<Number> first, @NonNull Expression<Number> second) {
+ return new Expression<>("/", first, second);
+ }
+
+ /**
+ * Returns the result of floating point division of the first input by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> division(@NonNull Number first, @NonNull Number second) {
+ return division(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the remainder after integer division of the first input by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> mod(@NonNull Expression<Number> first, @NonNull Expression<Number> second) {
+ return new Expression<>("%", first, second);
+ }
+
+ /**
+ * Returns the remainder after integer division of the first input by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> mod(@NonNull Number first, @NonNull Number second) {
+ return mod(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the result of raising the first input to the power specified by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> pow(@NonNull Expression<Number> first, @NonNull Expression<Number> second) {
+ return new Expression<>("^", first, second);
+ }
+
+ /**
+ * Returns the result of raising the first input to the power specified by the second.
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ */
+ public static Expression<Number> pow(@NonNull Number first, @NonNull Number second) {
+ return pow(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the square root of the input
+ *
+ * @param number the number to take the square root from
+ * @return expression
+ */
+ public static Expression<Number> sqrt(@NonNull Expression<Number> number) {
+ return new Expression<>("sqrt", number);
+ }
+
+ /**
+ * Returns the square root of the input
+ *
+ * @param number the number to take the square root from
+ * @return expression
+ */
+ public static Expression<Number> sqrt(@NonNull Number number) {
+ return sqrt(literal(number));
+ }
+
+ /**
+ * Returns the base-ten logarithm of the input.
+ *
+ * @param number the number to take base-ten logarithm from
+ * @return expression
+ */
+ public static Expression<Number> log10(@NonNull Expression<Number> number) {
+ return new Expression<>("log10", number);
+ }
+
+ /**
+ * Returns the base-ten logarithm of the input.
+ *
+ * @param number the number to take base-ten logarithm from
+ * @return expression
+ */
+ public static Expression<Number> log10(@NonNull Number number) {
+ return log10(literal(number));
+ }
+
+ /**
+ * Returns the natural logarithm of the input.
+ *
+ * @param number the number to take natural logarithm from
+ * @return expression
+ */
+ public static Expression<Number> ln(Expression<Number> number) {
+ return new Expression<>("ln", number);
+ }
+
+ /**
+ * Returns the natural logarithm of the input.
+ *
+ * @param number the number to take natural logarithm from
+ * @return expression
+ */
+ public static Expression<Number> ln(Number number) {
+ return ln(literal(number));
+ }
+
+ /**
+ * Returns the base-two logarithm of the input.
+ *
+ * @param number the number to take base-two logarithm from
+ * @return expression
+ */
+ public static Expression<Number> log2(@NonNull Expression<Number> number) {
+ return new Expression<>("log2", number);
+ }
+
+ /**
+ * Returns the base-two logarithm of the input.
+ *
+ * @param number the number to take base-two logarithm from
+ * @return expression
+ */
+ public static Expression<Number> log2(@NonNull Number number) {
+ return log2(literal(number));
+ }
+
+ /**
+ * Returns the sine of the input.
+ *
+ * @param number the number to calculate the sine for
+ * @return expression
+ */
+ public static Expression<Number> sin(@NonNull Expression<Number> number) {
+ return new Expression<>("sin", number);
+ }
+
+ /**
+ * Returns the sine of the input.
+ *
+ * @param number the number to calculate the sine for
+ * @return expression
+ */
+ public static Expression<Number> sin(@NonNull Number number) {
+ return sin(literal(number));
+ }
+
+ /**
+ * Returns the cosine of the input.
+ *
+ * @param number the number to calculate the cosine for
+ * @return expression
+ */
+ public static Expression<Number> cos(@NonNull Expression<Number> number) {
+ return new Expression<>("cos", number);
+ }
+
+ /**
+ * Returns the cosine of the input.
+ *
+ * @param number the number to calculate the cosine for
+ * @return expression
+ */
+ public static Expression<Number> cos(@NonNull Number number) {
+ return new Expression<>("cos", literal(number));
+ }
+
+ /**
+ * Returns the tangent of the input.
+ *
+ * @param number the number to calculate the tangent for
+ * @return expression
+ */
+ public static Expression<Number> tan(@NonNull Expression<Number> number) {
+ return new Expression<>("tan", number);
+ }
+
+ /**
+ * Returns the tangent of the input.
+ *
+ * @param number the number to calculate the tangent for
+ * @return expression
+ */
+ public static Expression<Number> tan(@NonNull Number number) {
+ return new Expression<>("tan", literal(number));
+ }
+
+ /**
+ * Returns the arcsine of the input.
+ *
+ * @param number the number to calculate the arcsine for
+ * @return expression
+ */
+ public static Expression<Number> asin(@NonNull Expression<Number> number) {
+ return new Expression<>("asin", number);
+ }
+
+ /**
+ * Returns the arcsine of the input.
+ *
+ * @param number the number to calculate the arcsine for
+ * @return expression
+ */
+ public static Expression<Number> asin(@NonNull Number number) {
+ return asin(literal(number));
+ }
+
+ /**
+ * Returns the arccosine of the input.
+ *
+ * @param number the number to calculate the arccosine for
+ * @return expression
+ */
+ public static Expression<Number> acos(@NonNull Expression<Number> number) {
+ return new Expression<>("acos", number);
+ }
+
+ /**
+ * Returns the arccosine of the input.
+ *
+ * @param number the number to calculate the arccosine for
+ * @return expression
+ */
+ public static Expression<Number> acos(@NonNull Number number) {
+ return acos(literal(number));
+ }
+
+ /**
+ * Returns the arctangent of the input.
+ *
+ * @param number the number to calculate the arctangent for
+ * @return expression
+ */
+ public static Expression<Number> atan(@NonNull Expression<Number> number) {
+ return new Expression("atan", number);
+ }
+
+ /**
+ * Returns the arctangent of the input.
+ *
+ * @param number the number to calculate the arctangent for
+ * @return expression
+ */
+ public static Expression<Number> atan(@NonNull Number number) {
+ return atan(literal(number));
+ }
+
+ /**
+ * Returns the minimum value of the inputs.
+ *
+ * @param numbers varargs of numbers to get the minimum from
+ * @return expression
+ */
+ public static Expression<Number> min(@Size(min = 1) Expression<Number>... numbers) {
+ return new Expression<>("min", numbers);
+ }
+
+ /**
+ * Returns the minimum value of the inputs.
+ *
+ * @param numbers varargs of numbers to get the minimum from
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> min(@Size(min = 1) Number... numbers) {
+ Expression<Number>[] numberExpression = (Expression<Number>[]) new Expression<?>[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return min(numberExpression);
+ }
+
+ /**
+ * Returns the maximum value of the inputs.
+ *
+ * @param numbers varargs of numbers to get the maximum from
+ * @return expression
+ */
+ public static Expression<Number> max(@Size(min = 1) Expression<Number>... numbers) {
+ return new Expression<>("max", numbers);
+ }
+
+ /**
+ * Returns the maximum value of the inputs.
+ *
+ * @param numbers varargs of numbers to get the maximum from
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<Number> max(@Size(min = 1) Number... numbers) {
+ Expression<Number>[] numberExpression = (Expression<Number>[]) new Expression<?>[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return max(numberExpression);
+ }
+
+ //
+ // String
+ //
+
+ /**
+ * Returns the input string converted to uppercase.
+ * <p>
+ * Follows the Unicode Default Case Conversion algorithm
+ * and the locale-insensitive case mappings in the Unicode Character Database.
+ * </p>
+ *
+ * @param string the string to upcase
+ * @return expression
+ */
+ public static Expression<String> upcase(@NonNull Expression<String> string) {
+ return new Expression<>("upcase", string);
+ }
+
+ /**
+ * Returns the input string converted to uppercase.
+ * <p>
+ * Follows the Unicode Default Case Conversion algorithm
+ * and the locale-insensitive case mappings in the Unicode Character Database.
+ * </p>
+ *
+ * @param string string to upcase
+ * @return expression
+ */
+ public static Expression<String> upcase(@NonNull String string) {
+ return upcase(literal(string));
+ }
+
+ /**
+ * Returns the input string converted to lowercase.
+ * <p>
+ * Follows the Unicode Default Case Conversion algorithm
+ * and the locale-insensitive case mappings in the Unicode Character Database.
+ * </p>
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<String> downcase(@NonNull Expression<String> input) {
+ return new Expression<>("downcase", input);
+ }
+
+ /**
+ * Returns the input string converted to lowercase.
+ * <p>
+ * Follows the Unicode Default Case Conversion algorithm
+ * and the locale-insensitive case mappings in the Unicode Character Database.
+ * </p>
+ *
+ * @param input string to downcase
+ * @return expression
+ */
+ public static Expression<String> downcase(@NonNull String input) {
+ return downcase(literal(input));
+ }
+
+ /**
+ * Returns a string consisting of the concatenation of the inputs.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<String> concat(@NonNull Expression<String>... input) {
+ return new Expression<>("concat", input);
+ }
+
+ /**
+ * Returns a string consisting of the concatenation of the inputs.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ @SuppressWarnings("unchecked")
+ public static Expression<String> concat(@NonNull String... input) {
+ Expression<String>[] stringExpression = (Expression<String>[]) new Expression<?>[input.length];
+ for (int i = 0; i < input.length; i++) {
+ stringExpression[i] = literal(input[i]);
+ }
+ return concat(stringExpression);
+ }
+
+ //
+ // Types
+ //
+
+ /**
+ * Asserts that the input is an array (optionally with a specific item type and length).
+ * If, when the input expression is evaluated, it is not of the asserted type,
+ * then this assertion will cause the whole expression to be aborted.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> array(@NonNull Expression input) {
+ return new Expression<>("array", input);
+ }
+
+ /**
+ * Returns a string describing the type of the given value.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<String> typeOf(@NonNull Expression input) {
+ return new Expression<>("typeof", input);
+ }
+
+ /**
+ * Asserts that the input value is a string.
+ * If multiple values are provided, each one is evaluated in order until a string value is obtained.
+ * If none of the inputs are strings, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> string(@NonNull Expression input) {
+ return new Expression<>("string", input);
+ }
+
+ /**
+ * Asserts that the input value is a number.
+ * If multiple values are provided, each one is evaluated in order until a number value is obtained.
+ * If none of the inputs are numbers, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> number(@NonNull Expression input) {
+ return new Expression<>("number", input);
+ }
+
+ /**
+ * Asserts that the input value is a boolean.
+ * If multiple values are provided, each one is evaluated in order until a boolean value is obtained.
+ * If none of the inputs are booleans, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> bool(@NonNull Expression input) {
+ return new Expression<>("boolean", input);
+ }
+
+ /**
+ * Asserts that the input value is an object. If it is not, the expression is an error
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> object(@NonNull Expression input) {
+ return new Expression<>("object", input);
+ }
+
+ /**
+ * Converts the input value to a string.
+ * If the input is null, the result is null.
+ * If the input is a boolean, the result is true or false.
+ * If the input is a number, it is converted to a string by NumberToString in the ECMAScript Language Specification.
+ * If the input is a color, it is converted to a string of the form "rgba(r,g,b,a)",
+ * where `r`, `g`, and `b` are numerals ranging from 0 to 255, and `a` ranges from 0 to 1.
+ * Otherwise, the input is converted to a string in the format specified by the JSON.stringify in the ECMAScript
+ * Language Specification.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<String> toString(@NonNull Expression input) {
+ return new Expression<>("to-string", input);
+ }
+
+ /**
+ * Converts the input value to a number, if possible.
+ * If the input is null or false, the result is 0.
+ * If the input is true, the result is 1.
+ * If the input is a string, it is converted to a number as specified by the ECMAScript Language Specification.
+ * If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained.
+ * If none of the inputs can be converted, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Number> toNumber(@NonNull Expression input) {
+ return new Expression<>("to-number", input);
+ }
+
+ /**
+ * "Converts the input value to a boolean. The result is `false` when then input is an empty string, 0, false,
+ * null, or NaN; otherwise it is true.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Boolean> toBool(@NonNull Expression input) {
+ return new Expression<>("to-boolean", input);
+ }
+
+ /**
+ * Converts the input value to a color. If multiple values are provided,
+ * each one is evaluated in order until the first successful conversion is obtained.
+ * If none of the inputs can be converted, the expression is an error.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression<Color> toColor(@NonNull Expression input) {
+ return new Expression<>("to-color", input);
+ }
+
+ //
+ // Variable binding
+ //
+
+ /**
+ * Binds input to named variables,
+ * which can then be referenced in the result expression using {@link #var(String)} or {@link #var(Expression)}.
+ *
+ * @param input expression input
+ * @return expression
+ */
+ public static Expression let(@Size(min = 1) Expression... input) {
+ return new Expression<>("let", input);
+ }
+
+ /**
+ * References variable bound using let.
+ *
+ * @param expression the variable naming expression that was bound with using let
+ * @return expression
+ */
+ public static Expression<Object> var(@NonNull Expression<String> expression) {
+ return new Expression<>("var", expression);
+ }
+
+ /**
+ * References variable bound using let.
+ *
+ * @param variableName the variable naming that was bound with using let
+ * @return expression
+ */
+ public static Expression var(@NonNull String variableName) {
+ return var(literal(variableName));
+ }
+
+ //
+ // Zoom
+ //
+
+ /**
+ * Gets the current zoom level.
+ * <p>
+ * Note that in style layout and paint properties,
+ * zoom may only appear as the input to a top-level step or interpolate expression.
+ * </p>
+ *
+ * @return expression
+ */
+ public static Expression<Number> zoom() {
+ return new Expression<>("zoom");
+ }
+
+ //
+ // Ramps, scales, curves
+ //
+
+ public static Stop stop(@NonNull Object stop, @NonNull Object value) {
+ return new Stop(stop, value);
+ }
+
+ /**
+ * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of
+ * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * Returns the output value of the stop just less than the input,
+ * or the first input if the input is less than the first stop.
+ *
+ * @param input the input value
+ * @param stops pair of input and output values
+ * @return expression
+ */
+ public static Expression step(@NonNull Number input, @NonNull Expression expression, Expression... stops) {
+ return step(literal(input), expression, stops);
+ }
+
+ /**
+ * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of
+ * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * Returns the output value of the stop just less than the input,
+ * or the first input if the input is less than the first stop.
+ *
+ * @param expression the input expression
+ * @param stops pair of input and output values
+ * @return expression
+ */
+ public static Expression step(@NonNull Expression<Number> input, @NonNull Expression expression, Expression... stops) {
+ return new Expression("step", join(new Expression[] {input, expression}, stops));
+ }
+
+ /**
+ * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of
+ * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * Returns the output value of the stop just less than the input,
+ * or the first input if the input is less than the first stop.
+ *
+ * @param input the input value
+ * @param stops pair of input and output values
+ * @return expression
+ */
+ public static Expression step(@NonNull Number input, @NonNull Expression expression, Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressions[i * 2] = literal(stops[i].value);
+ expressions[i * 2 + 1] = literal(stops[i].output);
+ }
+ return step(literal(input), expression, expressions);
+ }
+
+ /**
+ * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of
+ * input and output values (\"stops\"). The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * Returns the output value of the stop just less than the input,
+ * or the first input if the input is less than the first stop.
+ *
+ * @param input the input value
+ * @param stops pair of input and output values
+ * @return expression
+ */
+ public static Expression step(@NonNull Expression<Number> input, @NonNull Expression expression, Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressions[i * 2] = literal(stops[i].value);
+ expressions[i * 2 + 1] = literal(stops[i].output);
+ }
+ return step(input, expression, expressions);
+ }
+
+ /**
+ * Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\").
+ * The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * The output type must be `number`, `array&lt;number&gt;`, or `color`.
+ *
+ * @param interpolation type of interpolation
+ * @param number the input expression
+ * @param stops pair of input and output values
+ * @return expression
+ */
+ public static Expression interpolate(@NonNull Expression<Interpolator> interpolation,
+ @NonNull Expression<Number> number, Expression... stops) {
+ return new Expression("interpolate", join(new Expression[] {interpolation, number}, stops));
+ }
+
+ /**
+ * Produces continuous, smooth results by interpolating between pairs of input and output values (\"stops\").
+ * The `input` may be any numeric expression (e.g., `[\"get\", \"population\"]`).
+ * Stop inputs must be numeric literals in strictly ascending order.
+ * The output type must be `number`, `array&lt;number&gt;`, or `color`.
+ *
+ * @param interpolation type of interpolation
+ * @param number the input expression
+ * @param stops pair of input and output values
+ * @return expression
+ */
+ public static Expression interpolate(@NonNull Expression<Interpolator> interpolation,
+ @NonNull Expression<Number> number, Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressions[i * 2] = literal(stops[i].value);
+ expressions[i * 2 + 1] = literal(stops[i].output);
+ }
+ return interpolate(interpolation, number, expressions);
+ }
+
+ /**
+ * interpolates linearly between the pair of stops just less than and just greater than the input.
+ *
+ * @return expression
+ */
+ public static Expression<Interpolator> linear() {
+ return new Expression<>("linear");
+ }
+
+ /**
+ * Interpolates exponentially between the stops just less than and just greater than the input.
+ * `base` controls the rate at which the output increases:
+ * higher values make the output increase more towards the high end of the range.
+ * With values close to 1 the output increases linearly.
+ *
+ * @param base value controlling the route at which the output increases
+ * @return expression
+ */
+ public static Expression<Interpolator> exponential(@NonNull Number base) {
+ return exponential(literal(base));
+ }
+
+ /**
+ * Interpolates exponentially between the stops just less than and just greater than the input.
+ * The parameter controls the rate at which the output increases:
+ * higher values make the output increase more towards the high end of the range.
+ * With values close to 1 the output increases linearly.
+ *
+ * @param expression base number expression
+ * @return expression
+ */
+ public static Expression<Interpolator> exponential(@NonNull Expression<Number> expression) {
+ return new Expression<>("exponential", expression);
+ }
+
+ /**
+ * Interpolates using the cubic bezier curve defined by the given control points.
+ *
+ * @param x1 x value of the first point of a cubic bezier, ranges from 0 to 1
+ * @param y1 y value of the first point of a cubic bezier, ranges from 0 to 1
+ * @param x2 x value of the second point of a cubic bezier, ranges from 0 to 1
+ * @param y2 y value fo the second point of a cubic bezier, ranges from 0 to 1
+ * @return expression
+ */
+ public static Expression<Interpolator> cubicBezier(@NonNull Expression<Number> x1, @NonNull Expression<Number> y1,
+ @NonNull Expression<Number> x2, @NonNull Expression<Number> y2) {
+ return new Expression<>("cubic-bezier", x1, y1, x2, y2);
+ }
+
+ /**
+ * Interpolates using the cubic bezier curve defined by the given control points.
+ *
+ * @param x1 x value of the first point of a cubic bezier, ranges from 0 to 1
+ * @param y1 y value of the first point of a cubic bezier, ranges from 0 to 1
+ * @param x2 x value of the second point of a cubic bezier, ranges from 0 to 1
+ * @param y2 y value fo the second point of a cubic bezier, ranges from 0 to 1
+ * @return expression
+ */
+ public static Expression<Interpolator> cubicBezier(@NonNull Number x1, @NonNull Number y1,
+ @NonNull Number x2, @NonNull Number y2) {
+ return cubicBezier(literal(x1), literal(y1), literal(x2), literal(y2));
+ }
+
+ /**
+ * Joins two expressions arrays.
+ * <p>
+ * This flattens the object array output of an expression from a nested expression hierarchy.
+ * </p>
+ *
+ * @param left the left part of an expression
+ * @param right the right part of an expression
+ * @return the joined expression
+ */
+ private static Expression[] join(Expression[] left, Expression[] right) {
+ Expression[] output = new Expression[left.length + right.length];
+ System.arraycopy(left, 0, output, 0, left.length);
+ System.arraycopy(right, 0, output, left.length, right.length);
+ return output;
+ }
+
+} \ No newline at end of file
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
index e1e40821b1..e7bb52ebb3 100644
--- 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
@@ -279,7 +279,7 @@ public class Function<I, O> {
// noinspection unchecked
return (S) stops;
} catch (ClassCastException exception) {
- Timber.e(String.format("Stops: %s is a different type: %s", stops.getClass(), exception));
+ Timber.e(exception, "Stops: %s is a different type: ", stops.getClass());
return null;
}
}
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..324b845dda 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
@@ -8,8 +8,6 @@ import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-
/**
* The background color or pattern of the map.
*
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 1a7df06031..ea96e4e9c3 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
@@ -8,8 +8,6 @@ import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-
/**
* A filled circle.
*
@@ -278,6 +276,16 @@ public class CircleLayer extends Layer {
}
/**
+ * Get the CirclePitchAlignment property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getCirclePitchAlignment() {
+ return (PropertyValue<String>) new PropertyValue("circle-pitch-alignment", nativeGetCirclePitchAlignment());
+ }
+
+ /**
* Get the CircleStrokeWidth property
*
* @return property wrapper value around Float
@@ -411,6 +419,8 @@ public class CircleLayer extends Layer {
private native Object nativeGetCirclePitchScale();
+ private native Object nativeGetCirclePitchAlignment();
+
private native Object nativeGetCircleStrokeWidth();
private native TransitionOptions nativeGetCircleStrokeWidthTransition();
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 7807556b78..f77e7280f0 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
@@ -13,7 +13,16 @@ public class CustomLayer extends Layer {
long initializeFunction,
long renderFunction,
long deinitializeFunction) {
- initialize(id, initializeFunction, renderFunction, deinitializeFunction, context);
+ 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);
}
public CustomLayer(long nativePtr) {
@@ -24,7 +33,8 @@ public class CustomLayer extends Layer {
nativeUpdate();
}
- protected native void initialize(String id, long initializeFunction, long renderFunction, long deinitializeFunction,
+ protected native void initialize(String id, long initializeFunction, long renderFunction,
+ long contextLostFunction, long deinitializeFunction,
long context);
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..d4cdb2a425 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
@@ -8,8 +8,6 @@ import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-
/**
* An extruded (3D) polygon.
*
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..e639823ee1 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
@@ -8,8 +8,6 @@ import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-
/**
* A filled polygon with an optional stroked border.
*
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
index 5015dd009d..5400e04589 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.NonNull;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.functions.Function;
/**
@@ -88,6 +89,14 @@ public abstract class Layer {
}
private Object convertValue(Object value) {
- return value != null && value instanceof Function ? ((Function) value).toValueObject() : value;
+ if (value != null) {
+ if (value instanceof Function) {
+ return ((Function) value).toValueObject();
+ } else if (value instanceof Expression) {
+ return ((Expression) value).toArray();
+ }
+ }
+ return value;
}
-}
+
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
index ca106cc106..bbcfb98f07 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
@@ -8,8 +8,6 @@ import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-
/**
* A stroked line.
*
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 5e345268f9..8d6c7dd055 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
@@ -160,6 +160,88 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface ICON_TEXT_FIT {}
+ // ICON_ANCHOR: Part of the icon placed closest to the anchor.
+
+ /**
+ * The center of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_CENTER = "center";
+ /**
+ * The left side of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_LEFT = "left";
+ /**
+ * The right side of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_RIGHT = "right";
+ /**
+ * The top of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_TOP = "top";
+ /**
+ * The bottom of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_BOTTOM = "bottom";
+ /**
+ * The top left corner of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_TOP_LEFT = "top-left";
+ /**
+ * The top right corner of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_TOP_RIGHT = "top-right";
+ /**
+ * The bottom left corner of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_BOTTOM_LEFT = "bottom-left";
+ /**
+ * The bottom right corner of the icon is placed closest to the anchor.
+ */
+ public static final String ICON_ANCHOR_BOTTOM_RIGHT = "bottom-right";
+
+ /**
+ * Part of the icon placed closest to the anchor.
+ */
+ @StringDef({
+ ICON_ANCHOR_CENTER,
+ ICON_ANCHOR_LEFT,
+ ICON_ANCHOR_RIGHT,
+ ICON_ANCHOR_TOP,
+ ICON_ANCHOR_BOTTOM,
+ ICON_ANCHOR_TOP_LEFT,
+ ICON_ANCHOR_TOP_RIGHT,
+ ICON_ANCHOR_BOTTOM_LEFT,
+ ICON_ANCHOR_BOTTOM_RIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_ANCHOR {}
+
+ // ICON_PITCH_ALIGNMENT: Orientation of icon when map is pitched.
+
+ /**
+ * The icon is aligned to the plane of the map.
+ */
+ public static final String ICON_PITCH_ALIGNMENT_MAP = "map";
+ /**
+ * The icon is aligned to the plane of the viewport.
+ */
+ public static final String ICON_PITCH_ALIGNMENT_VIEWPORT = "viewport";
+ /**
+ * Automatically matches the value of {@link ICON_ROTATION_ALIGNMENT}.
+ */
+ public static final String ICON_PITCH_ALIGNMENT_AUTO = "auto";
+
+ /**
+ * Orientation of icon when map is pitched.
+ */
+ @StringDef({
+ ICON_PITCH_ALIGNMENT_MAP,
+ ICON_PITCH_ALIGNMENT_VIEWPORT,
+ ICON_PITCH_ALIGNMENT_AUTO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_PITCH_ALIGNMENT {}
+
// TEXT_PITCH_ALIGNMENT: Orientation of text when map is pitched.
/**
@@ -320,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.
@@ -332,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,
@@ -341,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.
@@ -353,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,
@@ -362,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.
@@ -374,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,
@@ -383,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.
@@ -395,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,
@@ -404,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.
@@ -416,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,
@@ -446,7 +528,28 @@ public final class Property {
@Retention(RetentionPolicy.SOURCE)
public @interface CIRCLE_PITCH_SCALE {}
- // FILL_EXTRUSION_TRANSLATE_ANCHOR: Controls the translation reference point.
+ // CIRCLE_PITCH_ALIGNMENT: Orientation of circle when map is pitched.
+
+ /**
+ * The circle is aligned to the plane of the map.
+ */
+ public static final String CIRCLE_PITCH_ALIGNMENT_MAP = "map";
+ /**
+ * The circle is aligned to the plane of the viewport.
+ */
+ public static final String CIRCLE_PITCH_ALIGNMENT_VIEWPORT = "viewport";
+
+ /**
+ * Orientation of circle when map is pitched.
+ */
+ @StringDef({
+ CIRCLE_PITCH_ALIGNMENT_MAP,
+ CIRCLE_PITCH_ALIGNMENT_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CIRCLE_PITCH_ALIGNMENT {}
+
+ // FILL_EXTRUSION_TRANSLATE_ANCHOR: Controls the frame of reference for `fill-extrusion-translate`.
/**
* The fill extrusion is translated relative to the map.
@@ -458,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,
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 73896b7901..fe164343b5 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
@@ -5,8 +5,11 @@ package com.mapbox.mapboxsdk.style.layers;
import android.annotation.SuppressLint;
import android.support.annotation.ColorInt;
-import com.mapbox.mapboxsdk.style.functions.Function;
import com.mapbox.mapboxsdk.style.functions.CameraFunction;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
+import com.mapbox.mapboxsdk.style.functions.Function;
+
+import java.util.Locale;
/**
* Constructs paint/layout properties for Layers
@@ -32,6 +35,7 @@ public class PropertyFactory {
* @param function the visibility function
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> visibility(Function<T, String> function) {
return new LayoutPropertyValue<>("visibility", function);
}
@@ -46,6 +50,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-antialias", value);
}
+ /**
+ * Whether or not the fill should be antialiased.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillAntialias(Expression expression) {
+ return new PaintPropertyValue<>("fill-antialias", expression);
+ }
+
/**
* Whether or not the fill should be antialiased.
@@ -54,6 +68,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Boolean
* @return property wrapper around a Boolean function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> fillAntialias(CameraFunction<Z, Boolean> function) {
return new PaintPropertyValue<>("fill-antialias", function);
}
@@ -68,6 +83,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-opacity", value);
}
+ /**
+ * The opacity of the entire fill layer. In contrast to the {@link PropertyFactory#fillColor}, this value will also affect the 1px stroke around the fill, if the stroke is used.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillOpacity(Expression expression) {
+ return new PaintPropertyValue<>("fill-opacity", expression);
+ }
+
/**
* The opacity of the entire fill layer. In contrast to the {@link PropertyFactory#fillColor}, this value will also affect the 1px stroke around the fill, if the stroke is used.
@@ -76,6 +101,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> fillOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("fill-opacity", function);
}
@@ -100,6 +126,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-color", value);
}
+ /**
+ * The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-color", expression);
+ }
+
/**
* The color of the filled part of this layer. This color can be specified as `rgba` with an alpha component and the color's opacity will not affect the opacity of the 1px stroke, if it is used.
@@ -108,6 +144,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> fillColor(Function<T, String> function) {
return new PaintPropertyValue<>("fill-color", function);
}
@@ -132,6 +169,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-outline-color", value);
}
+ /**
+ * The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillOutlineColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-outline-color", expression);
+ }
+
/**
* The outline color of the fill. Matches the value of {@link PropertyFactory#fillColor} if unspecified.
@@ -140,6 +187,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> fillOutlineColor(Function<T, String> function) {
return new PaintPropertyValue<>("fill-outline-color", function);
}
@@ -154,6 +202,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-translate", value);
}
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillTranslate(Expression expression) {
+ return new PaintPropertyValue<>("fill-translate", expression);
+ }
+
/**
* The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
@@ -162,12 +220,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> fillTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("fill-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#fillTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -176,14 +235,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#fillTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("fill-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#fillTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("fill-translate-anchor", function);
}
@@ -198,6 +268,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-pattern", value);
}
+ /**
+ * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillPattern(Expression expression) {
+ return new PaintPropertyValue<>("fill-pattern", expression);
+ }
+
/**
* Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
@@ -206,6 +286,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillPattern(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("fill-pattern", function);
}
@@ -220,6 +301,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-opacity", value);
}
+ /**
+ * The opacity at which the line will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineOpacity(Expression expression) {
+ return new PaintPropertyValue<>("line-opacity", expression);
+ }
+
/**
* The opacity at which the line will be drawn.
@@ -228,6 +319,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("line-opacity", function);
}
@@ -252,6 +344,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-color", value);
}
+ /**
+ * The color with which the line will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineColor(Expression expression) {
+ return new PaintPropertyValue<>("line-color", expression);
+ }
+
/**
* The color with which the line will be drawn.
@@ -260,6 +362,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> lineColor(Function<T, String> function) {
return new PaintPropertyValue<>("line-color", function);
}
@@ -274,6 +377,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-translate", value);
}
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineTranslate(Expression expression) {
+ return new PaintPropertyValue<>("line-translate", expression);
+ }
+
/**
* The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
@@ -282,12 +395,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> lineTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("line-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#lineTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -296,14 +410,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#lineTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("line-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#lineTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> lineTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("line-translate-anchor", function);
}
@@ -318,6 +443,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-width", value);
}
+ /**
+ * Stroke thickness.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineWidth(Expression expression) {
+ return new PaintPropertyValue<>("line-width", expression);
+ }
+
/**
* Stroke thickness.
@@ -326,6 +461,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("line-width", function);
}
@@ -340,6 +476,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-gap-width", value);
}
+ /**
+ * Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineGapWidth(Expression expression) {
+ return new PaintPropertyValue<>("line-gap-width", expression);
+ }
+
/**
* Draws a line casing outside of a line's actual path. Value indicates the width of the inner gap.
@@ -348,6 +494,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineGapWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("line-gap-width", function);
}
@@ -362,6 +509,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-offset", value);
}
+ /**
+ * The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a negative value results in an outset.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineOffset(Expression expression) {
+ return new PaintPropertyValue<>("line-offset", expression);
+ }
+
/**
* The line's offset. For linear features, a positive value offsets the line to the right, relative to the direction of the line, and a negative value to the left. For polygon features, a positive value results in an inset, and a negative value results in an outset.
@@ -370,6 +527,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineOffset(Function<T, Float> function) {
return new PaintPropertyValue<>("line-offset", function);
}
@@ -384,6 +542,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-blur", value);
}
+ /**
+ * Blur applied to the line, in density-independent pixels.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineBlur(Expression expression) {
+ return new PaintPropertyValue<>("line-blur", expression);
+ }
+
/**
* Blur applied to the line, in density-independent pixels.
@@ -392,6 +560,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> lineBlur(Function<T, Float> function) {
return new PaintPropertyValue<>("line-blur", function);
}
@@ -406,6 +575,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-dasharray", value);
}
+ /**
+ * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> lineDasharray(Expression expression) {
+ return new PaintPropertyValue<>("line-dasharray", expression);
+ }
+
/**
* Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width.
@@ -414,6 +593,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> lineDasharray(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("line-dasharray", function);
}
@@ -428,6 +608,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-pattern", value);
}
+ /**
+ * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> linePattern(Expression expression) {
+ return new PaintPropertyValue<>("line-pattern", expression);
+ }
+
/**
* Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512).
@@ -436,6 +626,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> linePattern(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("line-pattern", function);
}
@@ -450,6 +641,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-opacity", value);
}
+ /**
+ * The opacity at which the icon will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconOpacity(Expression expression) {
+ return new PaintPropertyValue<>("icon-opacity", expression);
+ }
+
/**
* The opacity at which the icon will be drawn.
@@ -458,6 +659,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> iconOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("icon-opacity", function);
}
@@ -482,6 +684,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-color", value);
}
+ /**
+ * The color of the icon. This can only be used with sdf icons.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconColor(Expression expression) {
+ return new PaintPropertyValue<>("icon-color", expression);
+ }
+
/**
* The color of the icon. This can only be used with sdf icons.
@@ -490,6 +702,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> iconColor(Function<T, String> function) {
return new PaintPropertyValue<>("icon-color", function);
}
@@ -514,6 +727,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-halo-color", value);
}
+ /**
+ * The color of the icon's halo. Icon halos can only be used with SDF icons.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconHaloColor(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-color", expression);
+ }
+
/**
* The color of the icon's halo. Icon halos can only be used with SDF icons.
@@ -522,6 +745,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> iconHaloColor(Function<T, String> function) {
return new PaintPropertyValue<>("icon-halo-color", function);
}
@@ -536,6 +760,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-halo-width", value);
}
+ /**
+ * Distance of halo to the icon outline.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconHaloWidth(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-width", expression);
+ }
+
/**
* Distance of halo to the icon outline.
@@ -544,6 +778,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> iconHaloWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("icon-halo-width", function);
}
@@ -558,6 +793,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-halo-blur", value);
}
+ /**
+ * Fade out the halo towards the outside.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconHaloBlur(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-blur", expression);
+ }
+
/**
* Fade out the halo towards the outside.
@@ -566,6 +811,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> iconHaloBlur(Function<T, Float> function) {
return new PaintPropertyValue<>("icon-halo-blur", function);
}
@@ -580,6 +826,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-translate", value);
}
+ /**
+ * Distance that the icon's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconTranslate(Expression expression) {
+ return new PaintPropertyValue<>("icon-translate", expression);
+ }
+
/**
* Distance that the icon's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
@@ -588,12 +844,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> iconTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("icon-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#iconTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -602,14 +859,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#iconTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> iconTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("icon-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#iconTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("icon-translate-anchor", function);
}
@@ -624,6 +892,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-opacity", value);
}
+ /**
+ * The opacity at which the text will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textOpacity(Expression expression) {
+ return new PaintPropertyValue<>("text-opacity", expression);
+ }
+
/**
* The opacity at which the text will be drawn.
@@ -632,6 +910,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> textOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("text-opacity", function);
}
@@ -656,6 +935,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-color", value);
}
+ /**
+ * The color with which the text will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textColor(Expression expression) {
+ return new PaintPropertyValue<>("text-color", expression);
+ }
+
/**
* The color with which the text will be drawn.
@@ -664,6 +953,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> textColor(Function<T, String> function) {
return new PaintPropertyValue<>("text-color", function);
}
@@ -688,6 +978,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-halo-color", value);
}
+ /**
+ * The color of the text's halo, which helps it stand out from backgrounds.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textHaloColor(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-color", expression);
+ }
+
/**
* The color of the text's halo, which helps it stand out from backgrounds.
@@ -696,6 +996,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> textHaloColor(Function<T, String> function) {
return new PaintPropertyValue<>("text-halo-color", function);
}
@@ -710,6 +1011,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-halo-width", value);
}
+ /**
+ * Distance of halo to the font outline. Max text halo width is 1/4 of the font-size.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textHaloWidth(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-width", expression);
+ }
+
/**
* Distance of halo to the font outline. Max text halo width is 1/4 of the font-size.
@@ -718,6 +1029,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> textHaloWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("text-halo-width", function);
}
@@ -732,6 +1044,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-halo-blur", value);
}
+ /**
+ * The halo's fadeout distance towards the outside.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textHaloBlur(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-blur", expression);
+ }
+
/**
* The halo's fadeout distance towards the outside.
@@ -740,6 +1062,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> textHaloBlur(Function<T, Float> function) {
return new PaintPropertyValue<>("text-halo-blur", function);
}
@@ -754,6 +1077,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-translate", value);
}
+ /**
+ * Distance that the text's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textTranslate(Expression expression) {
+ return new PaintPropertyValue<>("text-translate", expression);
+ }
+
/**
* Distance that the text's anchor is moved from its original placement. Positive values indicate right and down, while negative values indicate left and up.
@@ -762,12 +1095,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> textTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("text-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#textTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -776,14 +1110,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#textTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> textTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("text-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#textTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("text-translate-anchor", function);
}
@@ -798,6 +1143,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-radius", value);
}
+ /**
+ * Circle radius.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleRadius(Expression expression) {
+ return new PaintPropertyValue<>("circle-radius", expression);
+ }
+
/**
* Circle radius.
@@ -806,6 +1161,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleRadius(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-radius", function);
}
@@ -830,6 +1186,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-color", value);
}
+ /**
+ * The fill color of the circle.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleColor(Expression expression) {
+ return new PaintPropertyValue<>("circle-color", expression);
+ }
+
/**
* The fill color of the circle.
@@ -838,6 +1204,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> circleColor(Function<T, String> function) {
return new PaintPropertyValue<>("circle-color", function);
}
@@ -852,6 +1219,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-blur", value);
}
+ /**
+ * Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleBlur(Expression expression) {
+ return new PaintPropertyValue<>("circle-blur", expression);
+ }
+
/**
* Amount to blur the circle. 1 blurs the circle such that only the centerpoint is full opacity.
@@ -860,6 +1237,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleBlur(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-blur", function);
}
@@ -874,6 +1252,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-opacity", value);
}
+ /**
+ * The opacity at which the circle will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleOpacity(Expression expression) {
+ return new PaintPropertyValue<>("circle-opacity", expression);
+ }
+
/**
* The opacity at which the circle will be drawn.
@@ -882,6 +1270,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-opacity", function);
}
@@ -896,6 +1285,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-translate", value);
}
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleTranslate(Expression expression) {
+ return new PaintPropertyValue<>("circle-translate", expression);
+ }
+
/**
* The geometry's offset. Values are [x, y] where negatives indicate left and up, respectively.
@@ -904,12 +1303,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> circleTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("circle-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#circleTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -918,14 +1318,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#circleTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("circle-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#circleTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circleTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("circle-translate-anchor", function);
}
@@ -940,6 +1351,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-pitch-scale", value);
}
+ /**
+ * Controls the scaling behavior of the circle when the map is pitched.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circlePitchScale(Expression expression) {
+ return new PaintPropertyValue<>("circle-pitch-scale", expression);
+ }
+
/**
* Controls the scaling behavior of the circle when the map is pitched.
@@ -948,11 +1369,45 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circlePitchScale(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("circle-pitch-scale", function);
}
/**
+ * Orientation of circle when map is pitched.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> circlePitchAlignment(@Property.CIRCLE_PITCH_ALIGNMENT String value) {
+ return new PaintPropertyValue<>("circle-pitch-alignment", value);
+ }
+
+ /**
+ * Orientation of circle when map is pitched.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circlePitchAlignment(Expression expression) {
+ return new PaintPropertyValue<>("circle-pitch-alignment", expression);
+ }
+
+
+ /**
+ * Orientation of circle when map is pitched.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for String
+ * @return property wrapper around a String function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circlePitchAlignment(CameraFunction<Z, String> function) {
+ return new PaintPropertyValue<>("circle-pitch-alignment", function);
+ }
+
+ /**
* The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}.
*
* @param value a Float value
@@ -962,6 +1417,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-width", value);
}
+ /**
+ * The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleStrokeWidth(Expression expression) {
+ return new PaintPropertyValue<>("circle-stroke-width", expression);
+ }
+
/**
* The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}.
@@ -970,6 +1435,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleStrokeWidth(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-stroke-width", function);
}
@@ -994,6 +1460,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-color", value);
}
+ /**
+ * The stroke color of the circle.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleStrokeColor(Expression expression) {
+ return new PaintPropertyValue<>("circle-stroke-color", expression);
+ }
+
/**
* The stroke color of the circle.
@@ -1002,6 +1478,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> circleStrokeColor(Function<T, String> function) {
return new PaintPropertyValue<>("circle-stroke-color", function);
}
@@ -1016,6 +1493,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-opacity", value);
}
+ /**
+ * The opacity of the circle's stroke.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> circleStrokeOpacity(Expression expression) {
+ return new PaintPropertyValue<>("circle-stroke-opacity", expression);
+ }
+
/**
* The opacity of the circle's stroke.
@@ -1024,6 +1511,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> circleStrokeOpacity(Function<T, Float> function) {
return new PaintPropertyValue<>("circle-stroke-opacity", function);
}
@@ -1038,6 +1526,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-opacity", value);
}
+ /**
+ * The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionOpacity(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-opacity", expression);
+ }
+
/**
* The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
@@ -1046,6 +1544,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> fillExtrusionOpacity(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("fill-extrusion-opacity", function);
}
@@ -1070,6 +1569,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-color", value);
}
+ /**
+ * The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-color", expression);
+ }
+
/**
* The base color of the extruded fill. The extrusion's surfaces will be shaded differently based on this color in combination with the root `light` settings. If this color is specified as `rgba` with an alpha component, the alpha component will be ignored; use {@link PropertyFactory#fillExtrusionOpacity} to set layer opacity.
@@ -1078,6 +1587,7 @@ public class PropertyFactory {
* @param function a wrapper function for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> fillExtrusionColor(Function<T, String> function) {
return new PaintPropertyValue<>("fill-extrusion-color", function);
}
@@ -1092,6 +1602,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-translate", value);
}
+ /**
+ * The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionTranslate(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-translate", expression);
+ }
+
/**
* The geometry's offset. Values are [x, y] where negatives indicate left and up (on the flat plane), respectively.
@@ -1100,12 +1620,13 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float[]
* @return property wrapper around a Float[] function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> fillExtrusionTranslate(CameraFunction<Z, Float[]> function) {
return new PaintPropertyValue<>("fill-extrusion-translate", function);
}
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#fillExtrusionTranslate}.
*
* @param value a String value
* @return property wrapper around String
@@ -1114,14 +1635,25 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-translate-anchor", value);
}
+ /**
+ * Controls the frame of reference for {@link PropertyFactory#fillExtrusionTranslate}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-translate-anchor", expression);
+ }
+
/**
- * Controls the translation reference point.
+ * Controls the frame of reference for {@link PropertyFactory#fillExtrusionTranslate}.
*
* @param <Z> the zoom parameter type
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionTranslateAnchor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("fill-extrusion-translate-anchor", function);
}
@@ -1136,6 +1668,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-pattern", value);
}
+ /**
+ * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionPattern(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-pattern", expression);
+ }
+
/**
* Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
@@ -1144,6 +1686,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionPattern(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("fill-extrusion-pattern", function);
}
@@ -1158,6 +1701,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-height", value);
}
+ /**
+ * The height with which to extrude this layer.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionHeight(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-height", expression);
+ }
+
/**
* The height with which to extrude this layer.
@@ -1166,6 +1719,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> fillExtrusionHeight(Function<T, Float> function) {
return new PaintPropertyValue<>("fill-extrusion-height", function);
}
@@ -1180,6 +1734,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-base", value);
}
+ /**
+ * The height with which to extrude the base of this layer. Must be less than or equal to {@link PropertyFactory#fillExtrusionHeight}.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> fillExtrusionBase(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-base", expression);
+ }
+
/**
* The height with which to extrude the base of this layer. Must be less than or equal to {@link PropertyFactory#fillExtrusionHeight}.
@@ -1188,6 +1752,7 @@ public class PropertyFactory {
* @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, Float>> fillExtrusionBase(Function<T, Float> function) {
return new PaintPropertyValue<>("fill-extrusion-base", function);
}
@@ -1202,6 +1767,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-opacity", value);
}
+ /**
+ * The opacity at which the image will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterOpacity(Expression expression) {
+ return new PaintPropertyValue<>("raster-opacity", expression);
+ }
+
/**
* The opacity at which the image will be drawn.
@@ -1210,6 +1785,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterOpacity(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-opacity", function);
}
@@ -1224,6 +1800,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-hue-rotate", value);
}
+ /**
+ * Rotates hues around the color wheel.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterHueRotate(Expression expression) {
+ return new PaintPropertyValue<>("raster-hue-rotate", expression);
+ }
+
/**
* Rotates hues around the color wheel.
@@ -1232,6 +1818,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterHueRotate(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-hue-rotate", function);
}
@@ -1246,6 +1833,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-brightness-min", value);
}
+ /**
+ * Increase or reduce the brightness of the image. The value is the minimum brightness.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterBrightnessMin(Expression expression) {
+ return new PaintPropertyValue<>("raster-brightness-min", expression);
+ }
+
/**
* Increase or reduce the brightness of the image. The value is the minimum brightness.
@@ -1254,6 +1851,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterBrightnessMin(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-brightness-min", function);
}
@@ -1268,6 +1866,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-brightness-max", value);
}
+ /**
+ * Increase or reduce the brightness of the image. The value is the maximum brightness.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterBrightnessMax(Expression expression) {
+ return new PaintPropertyValue<>("raster-brightness-max", expression);
+ }
+
/**
* Increase or reduce the brightness of the image. The value is the maximum brightness.
@@ -1276,6 +1884,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterBrightnessMax(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-brightness-max", function);
}
@@ -1290,6 +1899,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-saturation", value);
}
+ /**
+ * Increase or reduce the saturation of the image.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterSaturation(Expression expression) {
+ return new PaintPropertyValue<>("raster-saturation", expression);
+ }
+
/**
* Increase or reduce the saturation of the image.
@@ -1298,6 +1917,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterSaturation(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-saturation", function);
}
@@ -1312,6 +1932,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-contrast", value);
}
+ /**
+ * Increase or reduce the contrast of the image.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterContrast(Expression expression) {
+ return new PaintPropertyValue<>("raster-contrast", expression);
+ }
+
/**
* Increase or reduce the contrast of the image.
@@ -1320,6 +1950,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterContrast(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-contrast", function);
}
@@ -1334,6 +1965,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-fade-duration", value);
}
+ /**
+ * Fade duration when a new tile is added.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> rasterFadeDuration(Expression expression) {
+ return new PaintPropertyValue<>("raster-fade-duration", expression);
+ }
+
/**
* Fade duration when a new tile is added.
@@ -1342,6 +1983,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterFadeDuration(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("raster-fade-duration", function);
}
@@ -1366,6 +2008,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("background-color", value);
}
+ /**
+ * The color with which the background will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> backgroundColor(Expression expression) {
+ return new PaintPropertyValue<>("background-color", expression);
+ }
+
/**
* The color with which the background will be drawn.
@@ -1374,6 +2026,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> backgroundColor(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("background-color", function);
}
@@ -1388,6 +2041,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("background-pattern", value);
}
+ /**
+ * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> backgroundPattern(Expression expression) {
+ return new PaintPropertyValue<>("background-pattern", expression);
+ }
+
/**
* Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512).
@@ -1396,6 +2059,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for String
* @return property wrapper around a String function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> backgroundPattern(CameraFunction<Z, String> function) {
return new PaintPropertyValue<>("background-pattern", function);
}
@@ -1410,6 +2074,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("background-opacity", value);
}
+ /**
+ * The opacity at which the background will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> backgroundOpacity(Expression expression) {
+ return new PaintPropertyValue<>("background-opacity", expression);
+ }
+
/**
* The opacity at which the background will be drawn.
@@ -1418,6 +2092,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for Float
* @return property wrapper around a Float function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> backgroundOpacity(CameraFunction<Z, Float> function) {
return new PaintPropertyValue<>("background-opacity", function);
}
@@ -1432,6 +2107,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-cap", value);
}
+ /**
+ * The display of line endings.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> lineCap(Expression value) {
+ return new LayoutPropertyValue<>("line-cap", value);
+ }
/**
@@ -1455,16 +2139,25 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-join", value);
}
+ /**
+ * The display of lines when joining.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> lineJoin(Expression value) {
+ return new LayoutPropertyValue<>("line-join", value);
+ }
/**
* The display of lines when joining.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
+ * @param <T> the function input type
+ * @param function a wrapper function for String
* @return property wrapper around a String function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> lineJoin(CameraFunction<Z, String> function) {
+ public static <T> PropertyValue<Function<T, String>> lineJoin(Function<T, String> function) {
return new LayoutPropertyValue<>("line-join", function);
}
@@ -1478,6 +2171,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-miter-limit", value);
}
+ /**
+ * Used to automatically convert miter joins to bevel joins for sharp angles.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> lineMiterLimit(Expression value) {
+ return new LayoutPropertyValue<>("line-miter-limit", value);
+ }
/**
@@ -1501,6 +2203,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-round-limit", value);
}
+ /**
+ * Used to automatically convert round joins to miter joins for shallow angles.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> lineRoundLimit(Expression value) {
+ return new LayoutPropertyValue<>("line-round-limit", value);
+ }
/**
@@ -1524,6 +2235,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("symbol-placement", value);
}
+ /**
+ * Label placement relative to its geometry.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> symbolPlacement(Expression value) {
+ return new LayoutPropertyValue<>("symbol-placement", value);
+ }
/**
@@ -1547,6 +2267,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("symbol-spacing", value);
}
+ /**
+ * Distance between two symbol anchors.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> symbolSpacing(Expression value) {
+ return new LayoutPropertyValue<>("symbol-spacing", value);
+ }
/**
@@ -1570,6 +2299,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("symbol-avoid-edges", value);
}
+ /**
+ * If true, the symbols will not cross tile edges to avoid mutual collisions. Recommended in layers that don't have enough padding in the vector tile to prevent collisions, or if it is a point symbol layer placed after a line symbol layer.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> symbolAvoidEdges(Expression value) {
+ return new LayoutPropertyValue<>("symbol-avoid-edges", value);
+ }
/**
@@ -1593,6 +2331,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-allow-overlap", value);
}
+ /**
+ * If true, the icon will be visible even if it collides with other previously drawn symbols.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> iconAllowOverlap(Expression value) {
+ return new LayoutPropertyValue<>("icon-allow-overlap", value);
+ }
/**
@@ -1616,6 +2363,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-ignore-placement", value);
}
+ /**
+ * If true, other symbols can be visible even if they collide with the icon.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> iconIgnorePlacement(Expression value) {
+ return new LayoutPropertyValue<>("icon-ignore-placement", value);
+ }
/**
@@ -1639,6 +2395,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-optional", value);
}
+ /**
+ * If true, text will display without their corresponding icons when the icon collides with other symbols and the text does not.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> iconOptional(Expression value) {
+ return new LayoutPropertyValue<>("icon-optional", value);
+ }
/**
@@ -1662,6 +2427,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-rotation-alignment", value);
}
+ /**
+ * In combination with {@link Property.SYMBOL_PLACEMENT}, determines the rotation behavior of icons.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconRotationAlignment(Expression value) {
+ return new LayoutPropertyValue<>("icon-rotation-alignment", value);
+ }
/**
@@ -1685,6 +2459,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-size", value);
}
+ /**
+ * Scales the original size of the icon by the provided factor. The new pixel size of the image will be the original pixel size multiplied by {@link PropertyFactory#iconSize}. 1 is the original size; 3 triples the size of the image.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> iconSize(Expression value) {
+ return new LayoutPropertyValue<>("icon-size", value);
+ }
/**
@@ -1708,6 +2491,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-text-fit", value);
}
+ /**
+ * Scales the icon to fit around the associated text.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconTextFit(Expression value) {
+ return new LayoutPropertyValue<>("icon-text-fit", value);
+ }
/**
@@ -1731,6 +2523,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-text-fit-padding", value);
}
+ /**
+ * Size of the additional area added to dimensions determined by {@link Property.ICON_TEXT_FIT}, in clockwise order: top, right, bottom, left.
+ *
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
+ */
+ public static PropertyValue<Expression> iconTextFitPadding(Expression value) {
+ return new LayoutPropertyValue<>("icon-text-fit-padding", value);
+ }
/**
@@ -1745,7 +2546,7 @@ public class PropertyFactory {
}
/**
- * Name of image in sprite to use for drawing an image background. A string with {tokens} replaced, referencing the data property to pull from.
+ * Name of image in sprite to use for drawing an image background. A string with `{tokens}` replaced, referencing the data property to pull from. (`{token}` replacement is only supported for literal {@link PropertyFactory#iconImage} values; not for property functions.)
*
* @param value a String value
* @return property wrapper around String
@@ -1754,10 +2555,19 @@ 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. (`{token}` replacement is only supported for literal {@link PropertyFactory#iconImage} values; not for property functions.)
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconImage(Expression value) {
+ return new LayoutPropertyValue<>("icon-image", value);
+ }
/**
- * Name of image in sprite to use for drawing an image background. A string with {tokens} replaced, referencing the data property to pull from.
+ * Name of image in sprite to use for drawing an image background. A string with `{tokens}` replaced, referencing the data property to pull from. (`{token}` replacement is only supported for literal {@link PropertyFactory#iconImage} values; not for property functions.)
*
* @param <T> the function input type
* @param function a wrapper function for String
@@ -1777,6 +2587,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-rotate", value);
}
+ /**
+ * Rotates the icon clockwise.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> iconRotate(Expression value) {
+ return new LayoutPropertyValue<>("icon-rotate", value);
+ }
/**
@@ -1800,6 +2619,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-padding", value);
}
+ /**
+ * Size of the additional area around the icon bounding box used for detecting symbol collisions.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> iconPadding(Expression value) {
+ return new LayoutPropertyValue<>("icon-padding", value);
+ }
/**
@@ -1823,6 +2651,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-keep-upright", value);
}
+ /**
+ * If true, the icon may be flipped to prevent it from being rendered upside-down.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> iconKeepUpright(Expression value) {
+ return new LayoutPropertyValue<>("icon-keep-upright", value);
+ }
/**
@@ -1846,6 +2683,15 @@ 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.
+ *
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
+ */
+ public static PropertyValue<Expression> iconOffset(Expression value) {
+ return new LayoutPropertyValue<>("icon-offset", value);
+ }
/**
@@ -1860,6 +2706,70 @@ public class PropertyFactory {
}
/**
+ * Part of the icon placed closest to the anchor.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> iconAnchor(@Property.ICON_ANCHOR String value) {
+ return new LayoutPropertyValue<>("icon-anchor", value);
+ }
+
+ /**
+ * Part of the icon placed closest to the anchor.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconAnchor(Expression value) {
+ return new LayoutPropertyValue<>("icon-anchor", value);
+ }
+
+
+ /**
+ * 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
+ */
+ public static <T> PropertyValue<Function<T, String>> iconAnchor(Function<T, String> function) {
+ return new LayoutPropertyValue<>("icon-anchor", function);
+ }
+
+ /**
+ * Orientation of icon when map is pitched.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> iconPitchAlignment(@Property.ICON_PITCH_ALIGNMENT String value) {
+ return new LayoutPropertyValue<>("icon-pitch-alignment", value);
+ }
+
+ /**
+ * Orientation of icon when map is pitched.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> iconPitchAlignment(Expression value) {
+ return new LayoutPropertyValue<>("icon-pitch-alignment", value);
+ }
+
+
+ /**
+ * 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
+ */
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconPitchAlignment(CameraFunction<Z, String> function) {
+ return new LayoutPropertyValue<>("icon-pitch-alignment", function);
+ }
+
+ /**
* Orientation of text when map is pitched.
*
* @param value a String value
@@ -1869,6 +2779,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-pitch-alignment", value);
}
+ /**
+ * Orientation of text when map is pitched.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textPitchAlignment(Expression value) {
+ return new LayoutPropertyValue<>("text-pitch-alignment", value);
+ }
/**
@@ -1892,6 +2811,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-rotation-alignment", value);
}
+ /**
+ * In combination with {@link Property.SYMBOL_PLACEMENT}, determines the rotation behavior of the individual glyphs forming the text.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textRotationAlignment(Expression value) {
+ return new LayoutPropertyValue<>("text-rotation-alignment", value);
+ }
/**
@@ -1906,7 +2834,7 @@ public class PropertyFactory {
}
/**
- * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal {@link PropertyFactory#textField} values--not for property functions.)
+ * Value to use for a text label. Feature properties are specified using tokens like `{field_name}`. (`{token}` replacement is only supported for literal {@link PropertyFactory#textField} values; not for property functions.)
*
* @param value a String value
* @return property wrapper around String
@@ -1915,10 +2843,19 @@ 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.)
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textField(Expression value) {
+ return new LayoutPropertyValue<>("text-field", value);
+ }
/**
- * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal {@link PropertyFactory#textField} values--not for property functions.)
+ * Value to use for a text label. Feature properties are specified using tokens like `{field_name}`. (`{token}` replacement is only supported for literal {@link PropertyFactory#textField} values; not for property functions.)
*
* @param <T> the function input type
* @param function a wrapper function for String
@@ -1938,16 +2875,25 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-font", value);
}
+ /**
+ * Font stack to use for displaying text.
+ *
+ * @param value a String[] value
+ * @return property wrapper around String[]
+ */
+ public static PropertyValue<Expression> textFont(Expression value) {
+ return new LayoutPropertyValue<>("text-font", value);
+ }
/**
* Font stack to use for displaying text.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String[]
+ * @param <T> the function input type
+ * @param function a wrapper function for String[]
* @return property wrapper around a String[] function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String[]>> textFont(CameraFunction<Z, String[]> function) {
+ public static <T> PropertyValue<Function<T, String[]>> textFont(Function<T, String[]> function) {
return new LayoutPropertyValue<>("text-font", function);
}
@@ -1961,6 +2907,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-size", value);
}
+ /**
+ * Font size.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textSize(Expression value) {
+ return new LayoutPropertyValue<>("text-size", value);
+ }
/**
@@ -1984,16 +2939,25 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-max-width", value);
}
+ /**
+ * The maximum line width for text wrapping.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textMaxWidth(Expression value) {
+ return new LayoutPropertyValue<>("text-max-width", value);
+ }
/**
* The maximum line width for text wrapping.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textMaxWidth(CameraFunction<Z, Float> function) {
+ public static <T> PropertyValue<Function<T, Float>> textMaxWidth(Function<T, Float> function) {
return new LayoutPropertyValue<>("text-max-width", function);
}
@@ -2007,6 +2971,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-line-height", value);
}
+ /**
+ * Text leading value for multi-line text.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textLineHeight(Expression value) {
+ return new LayoutPropertyValue<>("text-line-height", value);
+ }
/**
@@ -2030,16 +3003,25 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-letter-spacing", value);
}
+ /**
+ * Text tracking amount.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textLetterSpacing(Expression value) {
+ return new LayoutPropertyValue<>("text-letter-spacing", value);
+ }
/**
* Text tracking amount.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
* @return property wrapper around a Float function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textLetterSpacing(CameraFunction<Z, Float> function) {
+ public static <T> PropertyValue<Function<T, Float>> textLetterSpacing(Function<T, Float> function) {
return new LayoutPropertyValue<>("text-letter-spacing", function);
}
@@ -2053,16 +3035,25 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-justify", value);
}
+ /**
+ * Text justification options.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textJustify(Expression value) {
+ return new LayoutPropertyValue<>("text-justify", value);
+ }
/**
* Text justification options.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
+ * @param <T> the function input type
+ * @param function a wrapper function for String
* @return property wrapper around a String function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textJustify(CameraFunction<Z, String> function) {
+ public static <T> PropertyValue<Function<T, String>> textJustify(Function<T, String> function) {
return new LayoutPropertyValue<>("text-justify", function);
}
@@ -2076,16 +3067,25 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-anchor", value);
}
+ /**
+ * Part of the text placed closest to the anchor.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textAnchor(Expression value) {
+ return new LayoutPropertyValue<>("text-anchor", value);
+ }
/**
* Part of the text placed closest to the anchor.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
+ * @param <T> the function input type
+ * @param function a wrapper function for String
* @return property wrapper around a String function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textAnchor(CameraFunction<Z, String> function) {
+ public static <T> PropertyValue<Function<T, String>> textAnchor(Function<T, String> function) {
return new LayoutPropertyValue<>("text-anchor", function);
}
@@ -2099,6 +3099,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-max-angle", value);
}
+ /**
+ * Maximum angle change between adjacent characters.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textMaxAngle(Expression value) {
+ return new LayoutPropertyValue<>("text-max-angle", value);
+ }
/**
@@ -2122,6 +3131,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-rotate", value);
}
+ /**
+ * Rotates the text clockwise.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textRotate(Expression value) {
+ return new LayoutPropertyValue<>("text-rotate", value);
+ }
/**
@@ -2145,6 +3163,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-padding", value);
}
+ /**
+ * Size of the additional area around the text bounding box used for detecting symbol collisions.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Expression> textPadding(Expression value) {
+ return new LayoutPropertyValue<>("text-padding", value);
+ }
/**
@@ -2168,6 +3195,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-keep-upright", value);
}
+ /**
+ * If true, the text may be flipped vertically to prevent it from being rendered upside-down.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> textKeepUpright(Expression value) {
+ return new LayoutPropertyValue<>("text-keep-upright", value);
+ }
/**
@@ -2191,6 +3227,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-transform", value);
}
+ /**
+ * Specifies how to capitalize text, similar to the CSS {@link PropertyFactory#textTransform} property.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<Expression> textTransform(Expression value) {
+ return new LayoutPropertyValue<>("text-transform", value);
+ }
/**
@@ -2214,6 +3259,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-offset", value);
}
+ /**
+ * Offset distance of text from its anchor. Positive values indicate right and down, while negative values indicate left and up.
+ *
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
+ */
+ public static PropertyValue<Expression> textOffset(Expression value) {
+ return new LayoutPropertyValue<>("text-offset", value);
+ }
/**
@@ -2237,6 +3291,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-allow-overlap", value);
}
+ /**
+ * If true, the text will be visible even if it collides with other previously drawn symbols.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> textAllowOverlap(Expression value) {
+ return new LayoutPropertyValue<>("text-allow-overlap", value);
+ }
/**
@@ -2260,6 +3323,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-ignore-placement", value);
}
+ /**
+ * If true, other symbols can be visible even if they collide with the text.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> textIgnorePlacement(Expression value) {
+ return new LayoutPropertyValue<>("text-ignore-placement", value);
+ }
/**
@@ -2283,7 +3355,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-optional", value);
}
-
+ /**
+ * If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not.
+ *
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
+ */
+ public static PropertyValue<Expression> textOptional(Expression value) {
+ return new LayoutPropertyValue<>("text-optional", value);
+ }
/**
* If true, icons will display without their corresponding text when the text collides with other symbols and the icon does not.
@@ -2296,9 +3376,9 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-optional", function);
}
- @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/layers/PropertyValue.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java
index 68727c8a4f..a57c440df4 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
@@ -30,14 +30,29 @@ public class PropertyValue<T> {
this.value = value;
}
+ /**
+ * Returns if this is null
+ *
+ * @return true if this is null, false if not
+ */
public boolean isNull() {
return value == null;
}
+ /**
+ * Returns if this is a function.
+ *
+ * @return true if is a function, false if not
+ */
public boolean isFunction() {
return !isNull() && value instanceof Function;
}
+ /**
+ * Returns if this is a value.
+ *
+ * @return true if is a value, false if not
+ */
public boolean isValue() {
return !isNull() && !isFunction();
}
@@ -53,6 +68,11 @@ public class PropertyValue<T> {
}
}
+ /**
+ * Get the value of the property.
+ *
+ * @return the property value
+ */
@Nullable
public T getValue() {
if (isValue()) {
@@ -64,6 +84,11 @@ public class PropertyValue<T> {
}
}
+ /**
+ * Get the color int value of the property if the value is a color.
+ *
+ * @return the color int value of the property, null if not a color value
+ */
@ColorInt
@Nullable
public Integer getColorInt() {
@@ -80,6 +105,11 @@ public class PropertyValue<T> {
}
}
+ /**
+ * Get the string representation of a property value.
+ *
+ * @return the string representation
+ */
@Override
public String toString() {
return String.format("%s: %s", name, value);
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..0a056d8fac 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
@@ -2,14 +2,9 @@
package com.mapbox.mapboxsdk.style.layers;
-import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
-import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
-
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-
/**
* Raster map textures such as satellite imagery.
*
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 290e162da8..3d2c3881b2 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
@@ -8,8 +8,6 @@ import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-
/**
* An icon or a text label.
*
@@ -252,6 +250,26 @@ public class SymbolLayer extends Layer {
}
/**
+ * Get the IconAnchor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconAnchor() {
+ return (PropertyValue<String>) new PropertyValue("icon-anchor", nativeGetIconAnchor());
+ }
+
+ /**
+ * Get the IconPitchAlignment property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconPitchAlignment() {
+ return (PropertyValue<String>) new PropertyValue("icon-pitch-alignment", nativeGetIconPitchAlignment());
+ }
+
+ /**
* Get the TextPitchAlignment property
*
* @return property wrapper value around String
@@ -891,6 +909,10 @@ public class SymbolLayer extends Layer {
private native Object nativeGetIconOffset();
+ private native Object nativeGetIconAnchor();
+
+ private native Object nativeGetIconPitchAlignment();
+
private native Object nativeGetTextPitchAlignment();
private native Object nativeGetTextRotationAlignment();
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..ed138e557a 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
@@ -11,6 +11,7 @@ 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;
/**
* Constructs paint/layout properties for Layers
@@ -36,6 +37,7 @@ public class PropertyFactory {
* @param function the visibility function
* @return property wrapper around a String function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, String>> visibility(Function<T, String> function) {
return new LayoutPropertyValue<>("visibility", function);
}
@@ -63,6 +65,16 @@ public class PropertyFactory {
return new PaintPropertyValue<>("<%- property.name %>", value);
}
+ /**
+ * <%- propertyFactoryMethodDoc(property) %>
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> <%- camelizeWithLeadingLowercase(property.name) %>(Expression expression) {
+ return new PaintPropertyValue<>("<%- property.name %>", expression);
+ }
+
<% if (supportsPropertyFunction(property)) { -%>
/**
@@ -72,6 +84,7 @@ public class PropertyFactory {
* @param function a wrapper function for <%- propertyType(property) %>
* @return property wrapper around a <%- propertyType(property) %> function
*/
+ @Deprecated
public static <T> PropertyValue<Function<T, <%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(Function<T, <%- propertyType(property) %>> function) {
return new PaintPropertyValue<>("<%- property.name %>", function);
}
@@ -85,6 +98,7 @@ public class PropertyFactory {
* @param function a wrapper {@link CameraFunction} for <%- propertyType(property) %>
* @return property wrapper around a <%- propertyType(property) %> function
*/
+ @Deprecated
public static <Z extends Number> PropertyValue<CameraFunction<Z, <%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(CameraFunction<Z, <%- propertyType(property) %>> function) {
return new PaintPropertyValue<>("<%- property.name %>", function);
}
@@ -102,6 +116,15 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("<%- property.name %>", value);
}
+ /**
+ * <%- propertyFactoryMethodDoc(property) %>
+ *
+ * @param value a <%- propertyType(property) %> value
+ * @return property wrapper around <%- propertyType(property) %>
+ */
+ public static PropertyValue<Expression> <%- camelizeWithLeadingLowercase(property.name) %>(Expression value) {
+ return new LayoutPropertyValue<>("<%- property.name %>", value);
+ }
<% if (supportsPropertyFunction(property)) { -%>
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 b66a50b8a4..8f23e7d01e 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
@@ -13,7 +13,7 @@ import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
* The global light source.
*
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#light>">The online documentation</a>
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#light">The online documentation</a>
*/
@UiThread
public class Light {
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 215db03ad2..cd6218d3e2 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
@@ -18,7 +18,7 @@ public class Position {
private float polarAngle;
/**
- * Creates a Position from a radial coordinate, an azimuthal angle & a polar angle.
+ * 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°
@@ -31,7 +31,7 @@ public class Position {
}
/**
- * Returns a Position from a radial coordinate, an azimuthal angle & a polar angle
+ * Returns a Position from a radial coordinate, an azimuthal angle and a polar angle
*
* @param radialCoordinate the radial coordinate
* @param azimuthalAngle the azimuthal angle
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs
index 067efe1092..80d927128d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs
@@ -17,7 +17,7 @@ import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
* The global light source.
*
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#light>">The online documentation</a>
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#light">The online documentation</a>
*/
@UiThread
public class Light {
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..62f1719ddf
--- /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.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.style.layers.Filter;
+import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.FeatureCollection;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+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 GeoJsonOptions());
+ }
+
+ /**
+ * Create a CustomGeometrySource with non-default GeoJsonOptions.
+ * <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 GeoJsonOptions.
+ */
+ public CustomGeometrySource(String id, GeometryTileProvider provider, GeoJsonOptions options) {
+ this.provider = provider;
+ executor = Executors.newFixedThreadPool(4);
+ initialize(id, options);
+ }
+
+ /**
+ * Invalidate previously provided features within a given bounds at all zoom levels.
+ * Invoking this method will result in new requests to `GeometryTileProvider` for regions
+ * that contain, include, or intersect with the provided bounds.
+ *
+ * @param bounds The region in which features should be invalidated at all zoom levels
+ */
+ public void invalidateRegion(LatLngBounds bounds) {
+ nativeInvalidateBounds(bounds);
+ }
+
+ /**
+ * Invalidate the geometry contents of a specific tile. Invoking this method will result
+ * in new requests to `GeometryTileProvider` for visible tiles.
+ *
+ * @param zoomLevel Tile zoom level.
+ * @param x Tile X coordinate.
+ * @param y Tile Y coordinate.
+ */
+ public void invalidateTile(int zoomLevel, int x, int y) {
+ nativeInvalidateTile(zoomLevel, x, y);
+ }
+
+ /**
+ * Set or update geometry contents of a specific tile. Use this method to update tiles
+ * for which `GeometryTileProvider` was previously invoked. This method can be called from
+ * background threads.
+ *
+ * @param zoomLevel Tile zoom level.
+ * @param x Tile X coordinate.
+ * @param y Tile Y coordinate.
+ * @param data Feature collection for the tile.
+ */
+ public void setTileData(int zoomLevel, int x, int y, FeatureCollection data) {
+ nativeSetTileData(zoomLevel, x, y, data);
+ }
+
+ /**
+ * Queries the source for features.
+ *
+ * @param filter an optional filter statement to filter the returned Features
+ * @return the features
+ */
+ @NonNull
+ public List<Feature> querySourceFeatures(@Nullable Filter.Statement filter) {
+ Feature[] features = querySourceFeatures(filter != null ? filter.toArray() : null);
+ return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
+ }
+
+ protected native void initialize(String sourceId, Object options);
+
+ private native Feature[] querySourceFeatures(Object[] filter);
+
+ private native void nativeSetTileData(int z, int x, int y, FeatureCollection data);
+
+ private native void nativeInvalidateTile(int z, int x, int y);
+
+ private native void nativeInvalidateBounds(LatLngBounds bounds);
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+ private void setTileData(TileID tileId, FeatureCollection data) {
+ cancelledTileRequests.remove(tileId);
+ nativeSetTileData(tileId.z, tileId.x, tileId.y, data);
+ }
+
+ @WorkerThread
+ private void fetchTile(int z, int x, int y) {
+ AtomicBoolean cancelFlag = new AtomicBoolean(false);
+ TileID tileID = new TileID(z, x, y);
+ cancelledTileRequests.put(tileID, cancelFlag);
+ GeometryTileRequest request = new GeometryTileRequest(tileID, provider, this, cancelFlag);
+ executor.execute(request);
+ }
+
+ @WorkerThread
+ private void cancelTile(int z, int x, int y) {
+ AtomicBoolean cancelFlag = cancelledTileRequests.get(new TileID(z, x, y));
+ if (cancelFlag != null) {
+ cancelFlag.compareAndSet(false, true);
+ }
+ }
+
+ private static class TileID {
+ public int z;
+ public int x;
+ public int y;
+
+ public TileID(int _z, int _x, int _y) {
+ z = _z;
+ x = _x;
+ y = _y;
+ }
+
+ public int hashCode() {
+ return Arrays.hashCode(new int[]{z, x, y});
+ }
+
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (object == null || getClass() != object.getClass()) {
+ return false;
+ }
+
+ if (object instanceof TileID) {
+ TileID other = (TileID)object;
+ return this.z == other.z && this.x == other.x && this.y == other.y;
+ }
+ return false;
+ }
+ }
+
+ private static class GeometryTileRequest implements Runnable {
+ private TileID id;
+ private GeometryTileProvider provider;
+ private WeakReference<CustomGeometrySource> sourceRef;
+ private AtomicBoolean cancelled;
+
+ public GeometryTileRequest(TileID _id, GeometryTileProvider p,
+ CustomGeometrySource _source, AtomicBoolean _cancelled) {
+ id = _id;
+ provider = p;
+ sourceRef = new WeakReference<>(_source);
+ cancelled = _cancelled;
+ }
+
+ public void run() {
+ if (isCancelled()) {
+ return;
+ }
+
+ FeatureCollection data = provider.getFeaturesForBounds(LatLngBounds.from(id.z, id.x, id.y), id.z);
+ CustomGeometrySource source = sourceRef.get();
+ if (!isCancelled() && source != null && data != null) {
+ source.setTileData(id, data);
+ }
+ }
+
+ private Boolean isCancelled() {
+ return cancelled.get();
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
index 1a1711e547..81f7255b86 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
@@ -16,6 +16,17 @@ public class GeoJsonOptions extends HashMap<String, Object> {
* @param maxZoom 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
+ */
public GeoJsonOptions withMaxZoom(int maxZoom) {
this.put("maxzoom", maxZoom);
return this;
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..3f1eb315d3
--- /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.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.services.commons.geojson.FeatureCollection;
+
+/**
+ * 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/text/LocalGlyphRasterizer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java
new file mode 100644
index 0000000000..920a1270ac
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java
@@ -0,0 +1,46 @@
+package com.mapbox.mapboxsdk.text;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Bitmap;
+import android.graphics.Typeface;
+import android.support.annotation.WorkerThread;
+
+/**
+ * LocalGlyphRasterizer is the Android-specific platform implementation used
+ * by the portable local_glyph_rasterizer.hpp
+ */
+public class LocalGlyphRasterizer {
+
+ /***
+ * Uses Android-native drawing code to rasterize a single glyph
+ * to a square @{link Bitmap} which can be returned to portable
+ * code for transformation into a Signed Distance Field glyph.
+ *
+ * @param fontFamily Font family string to pass to Typeface.create
+ * @param bold If true, use Typeface.BOLD option
+ * @param glyphID 16-bit Unicode BMP codepoint to draw
+ *
+ * @return Return a @{link Bitmap} to be displayed in the requested tile.
+ */
+ @WorkerThread
+ protected static Bitmap drawGlyphBitmap(String fontFamily, boolean bold, char glyphID) {
+ /*
+ 35x35px dimensions are hardwired to match local_glyph_rasterizer.cpp
+ These dimensions are large enough to draw a 24 point character in the middle
+ of the bitmap (y: 20) with some buffer around the edge
+ */
+ Bitmap bitmap = Bitmap.createBitmap(35, 35, Bitmap.Config.ARGB_8888);
+
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setTextSize(24);
+ paint.setTypeface(Typeface.create(fontFamily, bold ? Typeface.BOLD : Typeface.NORMAL));
+
+ Canvas canvas = new Canvas();
+ canvas.setBitmap(bitmap);
+ canvas.drawText(String.valueOf(glyphID), 0, 20, paint);
+
+ return bitmap;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java
index e3fc765734..78503f9dfe 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java
@@ -1,12 +1,27 @@
package com.mapbox.mapboxsdk.utils;
+import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.view.View;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Utility class for creating bitmaps
+ */
public class BitmapUtils {
+ /**
+ * Convert a view to a bitmap.
+ *
+ * @param view the view to convert
+ * @return the converted bitmap
+ */
public static Bitmap createBitmapFromView(@NonNull View view) {
view.setDrawingCacheEnabled(true);
view.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW);
@@ -22,6 +37,13 @@ public class BitmapUtils {
return snapshot;
}
+ /**
+ * Create a bitmap from a background and a foreground bitmap
+ *
+ * @param background The bitmap placed in the background
+ * @param foreground The bitmap placed in the foreground
+ * @return the merged bitmap
+ */
public static Bitmap mergeBitmap(@NonNull Bitmap background, @NonNull Bitmap foreground) {
Bitmap result = Bitmap.createBitmap(background.getWidth(), background.getHeight(), background.getConfig());
Canvas canvas = new Canvas(result);
@@ -30,4 +52,69 @@ public class BitmapUtils {
return result;
}
+ /**
+ * Extract an underlying bitmap from a drawable
+ *
+ * @param sourceDrawable The source drawable
+ * @return The underlying bitmap
+ */
+ public static Bitmap getBitmapFromDrawable(Drawable sourceDrawable) {
+ if (sourceDrawable == null) {
+ return null;
+ }
+
+ if (sourceDrawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) sourceDrawable).getBitmap();
+ } else {
+ //copying drawable object to not manipulate on the same reference
+ Drawable.ConstantState constantState = sourceDrawable.getConstantState();
+ if (constantState == null) {
+ return null;
+ }
+ Drawable drawable = constantState.newDrawable().mutate();
+
+ Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+ }
+
+ /**
+ * Create a byte array out of drawable
+ *
+ * @param drawable The source drawable
+ * @return The byte array of source drawable
+ */
+ public static byte[] getByteArrayFromDrawable(Drawable drawable) {
+ if (drawable == null) {
+ return null;
+ }
+
+ Bitmap bitmap = getBitmapFromDrawable(drawable);
+ if (bitmap == null) {
+ return null;
+ }
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
+ return stream.toByteArray();
+ }
+
+ /**
+ * Decode byte array to drawable object
+ *
+ * @param context Context to obtain {@link android.content.res.Resources}
+ * @param array The source byte array
+ * @return The drawable created from source byte array
+ */
+ public static Drawable getDrawableFromByteArray(Context context, byte[] array) {
+ if (array == null) {
+ return null;
+ }
+ Bitmap compass = BitmapFactory.decodeByteArray(array, 0, array.length);
+ return new BitmapDrawable(context.getResources(), compass);
+ }
+
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java
index 24c76243d9..1c0e439afc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java
@@ -5,6 +5,7 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.v4.graphics.drawable.DrawableCompat;
@@ -23,49 +24,64 @@ import java.util.regex.Pattern;
public class ColorUtils {
/**
- * Returns a color integer associated as primary color from a theme based on a {@link Context}.
+ * Returns a color integer associated as primary color from a theme based on a Context.
*
* @param context The context used to style the color attributes.
* @return The primary color value of current theme in the form 0xAARRGGBB.
*/
@ColorInt
public static int getPrimaryColor(@NonNull Context context) {
- TypedValue typedValue = new TypedValue();
- Resources.Theme theme = context.getTheme();
- theme.resolveAttribute(R.attr.colorPrimary, typedValue, true);
- return typedValue.data;
+ try {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ int id = context.getResources().getIdentifier("colorPrimary", "attrs", context.getPackageName());
+ theme.resolveAttribute(id, typedValue, true);
+ return typedValue.data;
+ } catch (Exception exception) {
+ return getColorCompat(context, R.color.mapbox_blue);
+ }
}
/**
- * Returns a color integer associated as primary dark color from a theme based on a {@link Context}.
+ * Returns a color integer associated as primary dark color from a theme based on a Context.
*
* @param context The context used to style the color attributes.
* @return The primary dark color value of current theme in the form 0xAARRGGBB.
*/
@ColorInt
public static int getPrimaryDarkColor(@NonNull Context context) {
- TypedValue typedValue = new TypedValue();
- Resources.Theme theme = context.getTheme();
- theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
- return typedValue.data;
+ try {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ int id = context.getResources().getIdentifier("colorPrimaryDark", "attrs", context.getPackageName());
+ theme.resolveAttribute(id, typedValue, true);
+ return typedValue.data;
+ } catch (Exception exception) {
+ return getColorCompat(context, R.color.mapbox_blue);
+ }
}
/**
- * Returns a color integer associated as accent color from a theme based on a {@link Context}.
+ * Returns a color integer associated as accent color from a theme based on a Context.
*
* @param context The context used to style the color attributes.
* @return The accent color value of current theme in the form 0xAARRGGBB.
*/
@ColorInt
public static int getAccentColor(@NonNull Context context) {
- TypedValue typedValue = new TypedValue();
- Resources.Theme theme = context.getTheme();
- theme.resolveAttribute(R.attr.colorAccent, typedValue, true);
- return typedValue.data;
+ try {
+ TypedValue typedValue = new TypedValue();
+ Resources.Theme theme = context.getTheme();
+ int id = context.getResources().getIdentifier("colorAccent", "attrs", context.getPackageName());
+ theme.resolveAttribute(id, typedValue, true);
+ return typedValue.data;
+ } catch (Exception exception) {
+ return getColorCompat(context, R.color.mapbox_gray);
+ }
}
/**
- * Returns a color state list associated with a theme based on a {@link Context}
+ * Returns a color state list associated with a theme based on a Context.
*
* @param color The color used for tinting.
* @return A ColorStateList object containing the primary color of a theme
@@ -85,7 +101,7 @@ public class ColorUtils {
}
/**
- * Set a color tint list to the {@link Drawable} of an {@link ImageView}.
+ * Set a color tint list to the Drawable of an ImageView.
*
* @param imageView The view to set the default tint list.
* @param tintColor The color to tint.
@@ -122,4 +138,12 @@ public class ColorUtils {
throw new ConversionException("Not a valid rgb/rgba value");
}
}
+
+ private static int getColorCompat(Context context, int id) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ return context.getResources().getColor(id, context.getTheme());
+ } else {
+ return context.getResources().getColor(id);
+ }
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java
new file mode 100644
index 0000000000..c7d7a13a3d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java
@@ -0,0 +1,27 @@
+package com.mapbox.mapboxsdk.utils;
+
+/**
+ * Comparisons from std sdk, which aren't available in API level <= 15
+ */
+public class Compare {
+
+ /**
+ * @see Integer#compare(int, int)
+ * @param x left side
+ * @param y right side
+ * @return std compare value
+ */
+ public static int compare(int x, int y) {
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
+ }
+
+ /**
+ * @see Boolean#compare(boolean, boolean)
+ * @param x left side
+ * @param y right side
+ * @return std compare value
+ */
+ public static int compare(boolean x, boolean y) {
+ return (x == y) ? 0 : (x ? 1 : -1);
+ }
+}
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 f810d6231d..007880acd1 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
@@ -18,12 +18,25 @@ import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
*/
public class MapFragmentUtils {
+ /**
+ * Convert MapboxMapOptions to a bundle of fragment arguments.
+ *
+ * @param options The MapboxMapOptions to convert
+ * @return a bundle of converted fragment arguments
+ */
public static Bundle createFragmentArgs(MapboxMapOptions options) {
Bundle bundle = new Bundle();
bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, options);
return bundle;
}
+ /**
+ * Convert a bundle of fragment arguments to MapboxMapOptions.
+ *
+ * @param context The context of the activity hosting the fragment
+ * @param args The fragment arguments
+ * @return converted MapboxMapOptions
+ */
public static MapboxMapOptions resolveArgs(Context context, Bundle args) {
MapboxMapOptions options;
if (args != null && args.containsKey(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS)) {
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 7294f43c02..412d8c5d9b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
@@ -11,6 +11,7 @@
<!--Configuration-->
<public name="mapbox_styleUrl" type="attr" />
<public name="mapbox_apiBaseUrl" type="attr" />
+ <public name="mapbox_localIdeographFontFamily" type="attr" />
<!--Camera-->
<public name="mapbox_cameraTargetLng" type="attr" />
@@ -20,7 +21,7 @@
<public name="mapbox_cameraTilt" type="attr" />
<!--Zoom-->
- <public name="mapbox_cameraZoomMax" ftype="attr" />
+ <public name="mapbox_cameraZoomMax" type="attr" />
<public name="mapbox_cameraZoomMin" type="attr" />
<!--Gestures-->
@@ -72,19 +73,24 @@
<public name="mapbox_uiAttributionMarginRight" type="attr" />
<public name="mapbox_uiAttributionMarginBottom" type="attr" />
- <!-- Deprecated to use TextureView-->
+ <!-- Use TextureView-->
<public name="mapbox_renderTextureMode" type="attr" />
+ <public name="mapbox_enableTilePrefetch" type="attr" />
+ <public name="mapbox_enableZMediaOverlay" type="attr" />
+
<!-- Exposed content descriptions -->
<public name="mapbox_logoContentDescription" type="string" />
<!-- Exposed styles -->
<public name="mapbox_style_mapbox_streets" type="string" />
- <public name="mapbox_style_emerald" type="string" />
+ <public name="mapbox_style_outdoors" type="string" />
<public name="mapbox_style_light" type="string" />
<public name="mapbox_style_dark" type="string" />
<public name="mapbox_style_satellite" type="string" />
<public name="mapbox_style_satellite_streets" type="string" />
+ <public name="mapbox_style_traffic_day" type="string" />
+ <public name="mapbox_style_traffic_night" type="string" />
<!-- Exposed strings -->
<public name="mapbox_compassContentDescription" type="string" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml
deleted file mode 100644
index 4c733ed112..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?attr/colorPrimaryDark" android:state_pressed="true" />
- <item android:color="?attr/colorPrimary" />
-</selector>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-v21/mapbox_default_bg_selector.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-v21/mapbox_default_bg_selector.xml
deleted file mode 100644
index 2fd6815c23..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-v21/mapbox_default_bg_selector.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?android:colorControlHighlight">
- <item android:id="@android:id/mask">
- <shape android:shape="oval">
- <solid android:color="@android:color/white" />
- </shape>
- </item>
-</ripple>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mapview_preview.jpg b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mapview_preview.jpg
deleted file mode 100644
index 3a21fc08bc..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mapview_preview.jpg
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png
deleted file mode 100644
index 584b320299..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_default_bg_selector.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_default_bg_selector.xml
deleted file mode 100644
index 3efe48615b..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_default_bg_selector.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/mapbox_gray_light" android:state_pressed="true" />
- <item android:drawable="@color/mapbox_gray_light" android:state_focused="true" />
- <item android:drawable="@android:color/transparent" />
-</selector>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml
index 763bb118e0..f275860d59 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml
@@ -9,5 +9,6 @@
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:textAllCaps="true"
+ android:textIsSelectable="false"
android:textAppearance="?android:attr/textAppearanceButton"
android:textColor="@color/mapbox_blue"/>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml
index 26c974dc0d..3a35396257 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml
@@ -23,6 +23,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
+ android:textIsSelectable="false"
android:maxEms="17"
android:textColor="@android:color/black"
android:textSize="18sp"
@@ -34,13 +35,13 @@
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
+ android:textIsSelectable="false"
android:lineSpacingExtra="1dp"
android:maxEms="17"
android:textColor="@color/mapbox_gray"
android:textSize="14sp"/>
<TextView
- android:id="@+id/infowindow_subdescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxEms="17"
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 6d07de7baa..df7ccaaca9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <SurfaceView
+ <android.opengl.GLSurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -28,6 +28,7 @@
android:contentDescription="@string/mapbox_compassContentDescription"/>
<ImageView
+ android:visibility="gone"
android:id="@+id/logoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -35,6 +36,7 @@
android:src="@drawable/mapbox_logo_icon"/>
<ImageView
+ android:visibility="gone"
android:id="@+id/attributionView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml
deleted file mode 100644
index 1c1ab0e71b..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_preview.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- android:contentDescription="@null"
- android:src="@drawable/mapbox_mapview_preview"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"/>
-
- <ImageView
- android:id="@id/logoView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_marginBottom="@dimen/mapbox_four_dp"
- android:layout_marginLeft="@dimen/mapbox_four_dp"
- android:layout_marginStart="@dimen/mapbox_four_dp"
- android:contentDescription="@null"
- android:src="@drawable/mapbox_logo_icon"/>
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_toEndOf="@+id/logoView"
- android:contentDescription="@null"
- android:layout_marginBottom="@dimen/mapbox_four_dp"
- android:background="@drawable/mapbox_default_bg_selector"
- android:src="@drawable/mapbox_info_bg_selector"
- android:layout_marginLeft="@dimen/mapbox_four_dp"
- android:layout_marginStart="@dimen/mapbox_four_dp"
- android:layout_toRightOf="@+id/logoView"/>
-
- <ImageView
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_margin="10dp"
- android:contentDescription="@null"
- android:src="@drawable/mapbox_compass_icon"/>
-
-</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml
index 7e4a079063..51eb46e1d5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ android:id="@+id/image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@null"/>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-bg/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-bg/strings.xml
new file mode 100644
index 0000000000..262e94f368
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-bg/strings.xml
@@ -0,0 +1,15 @@
+<?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_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_offline_error_region_definition_invalid">Предоставените OfflineRegionDefinition не пасват в границите на света: %s</string>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml
index 34e9914e46..6ed788d1dc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml
@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="mapbox_compassContentDescription">Brúixola del mapa. Activa per restablir la rotació del mapa al Nord.</string>
- <string name="mapbox_attributionsIconContentDescription">Icona d\'atribució. Activa per mostrar el diàleg de l\'atribució.</string>
+ <string name="mapbox_attributionsIconContentDescription">Icona d’atribució. Activa per mostrar el diàleg de l’atribució.</string>
<string name="mapbox_myLocationViewContentDescription">Vista de posició. Mostra la teva posició al mapa.</string>
- <string name="mapbox_mapActionDescription">Mostrant un mapa creat amb Mapbox. Desplaça\'t arrossegant amb dos dits. Fes zoom pessigant amb dos dits.</string>
+ <string name="mapbox_mapActionDescription">Mostrant un mapa creat amb Mapbox. Desplaça’t arrossegant amb dos dits. Fes zoom pessigant amb dos dits.</string>
<string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
<string name="mapbox_attributionTelemetryTitle">Millora els mapes de Mapbox</string>
- <string name="mapbox_attributionTelemetryMessage">Estàs ajudant a millorar els mapes d\'OpenStreetMap i de Mapbox aportant dades d\'ús anònimes.</string>
- <string name="mapbox_attributionTelemetryPositive">D\'acord</string>
+ <string name="mapbox_attributionTelemetryMessage">Estàs ajudant a millorar els mapes d’OpenStreetMap i de Mapbox aportant dades d’ús anònimes.</string>
+ <string name="mapbox_attributionTelemetryPositive">D’acord</string>
<string name="mapbox_attributionTelemetryNegative">Disconforme</string>
<string name="mapbox_attributionTelemetryNeutral">Més informació</string>
<string name="mapbox_offline_error_region_definition_invalid">La OfflineRegionDefinition proporcionada no encaixa amb els límits del món: %s</string>
-</resources>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-es/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-es/strings.xml
index 92c055223f..9844642381 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-es/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-es/strings.xml
@@ -4,12 +4,13 @@
<string name="mapbox_attributionsIconContentDescription">Ícono de atribución. Actívalo para mostrar el diálogo de atribución.</string>
<string name="mapbox_myLocationViewContentDescription">Vista de ubicación. Muestra tu ubicación en el mapa.</string>
<string name="mapbox_mapActionDescription">Se está mostrando un mapa creado con Mapbox. Arrastra dos dedos para desplazarte o pellizca para acercar.</string>
- <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Maps SDK para Android</string>
<string name="mapbox_attributionTelemetryTitle">Ayúdanos a mejorar los mapas de Mapbox</string>
<string name="mapbox_attributionTelemetryMessage">Gracias a tu contribución de datos anónimos de uso, ayudas a mejorar OpenStreetMap y Mapbox.</string>
<string name="mapbox_attributionTelemetryPositive">Aceptar</string>
<string name="mapbox_attributionTelemetryNegative">Rechazar</string>
<string name="mapbox_attributionTelemetryNeutral">Más información</string>
+ <string name="mapbox_attributionErrorNoBrowser">No puede abrir la página Web porque no hay un navegador Web en el dispositivo.</string>
<string name="mapbox_offline_error_region_definition_invalid">El parámetro OfflineRegionDefinition que se ingresó no coincide con los límites mundiales: %s</string>
-
+ <string name="mapbox_telemetrySettings">Ajustes de telemetría</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
new file mode 100644
index 0000000000..48d90c3324
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml
@@ -0,0 +1,15 @@
+<?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_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_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_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>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-hu/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-hu/strings.xml
new file mode 100644
index 0000000000..520edb2733
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-hu/strings.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="mapbox_myLocationViewContentDescription">Hely nézet. Megmutatja, hol vagy a térképen.</string>
+ <string name="mapbox_mapActionDescription">Egy Mapbox-szal készült térkép megjelenítése. Húzd két ujjadat a görgetéshez. Csippentsd össze a nagyításhoz.</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionTelemetryTitle">Tedd jobbá a Mapbox térképeket</string>
+ <string name="mapbox_attributionTelemetryMessage">Segítesz az OpenStreetMap és Mapbox térképek jobbá tételében névtelen felhasználási adatok elküldésével.</string>
+ <string name="mapbox_attributionTelemetryPositive">Egyetértek</string>
+ <string name="mapbox_attributionTelemetryNegative">Nem értek egyet</string>
+ <string name="mapbox_attributionTelemetryNeutral">Több infó</string>
+ <string name="mapbox_offline_error_region_definition_invalid">A megadott OfflineRegionDefinition nem fér bele a világ kereteibe: %s</string>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml
index fef652c542..f1fcf837ea 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml
@@ -11,4 +11,5 @@
<string name="mapbox_attributionTelemetryNegative">Intrekken</string>
<string name="mapbox_attributionTelemetryNeutral">Meer informatie</string>
<string name="mapbox_offline_error_region_definition_invalid">Aangeleverde OfflineRegionDefinition past niet in de wereld omtrek: %s</string>
-</resources>
+
+ </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
new file mode 100644
index 0000000000..39880d56ba
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml
@@ -0,0 +1,15 @@
+<?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_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_offline_error_region_definition_invalid">Запрошенный OfflineRegionDefinition не входит в допустимые границы: %s</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
new file mode 100644
index 0000000000..64c7e3efcc
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml
@@ -0,0 +1,15 @@
+<?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_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_offline_error_region_definition_invalid">Межі ділянки для оффлайнового користування даними за межами світу: %s</string>
+
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-vi/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-vi/strings.xml
index a0cad6487a..77e72a4db7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-vi/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-vi/strings.xml
@@ -4,12 +4,13 @@
<string name="mapbox_attributionsIconContentDescription">Biểu tượng ghi công. Kích hoạt để xem hộp thoại ghi công.</string>
<string name="mapbox_myLocationViewContentDescription">Cái chỉ vị trí. Cái này chỉ vị trí của bạn trên bản đồ.</string>
<string name="mapbox_mapActionDescription">Đang xem bản đồ được xây dựng dùng Mapbox. Kéo hai ngón tay để cuộn. Chụm các ngón tay lại để phóng to. Tách các ngón tay ra để thu nhỏ.</string>
- <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Maps SDK cho Android</string>
<string name="mapbox_attributionTelemetryTitle">Cải tiến các Bản đồ Mapbox</string>
<string name="mapbox_attributionTelemetryMessage">Bạn đang giúp cải tiến các bản đồ OpenStreetMap và Mapbox bằng cách đóng góp dữ liệu vô danh hóa về cách sử dụng.</string>
<string name="mapbox_attributionTelemetryPositive">Đồng ý</string>
<string name="mapbox_attributionTelemetryNegative">Phản đối</string>
<string name="mapbox_attributionTelemetryNeutral">Thông tin thêm</string>
+ <string name="mapbox_attributionErrorNoBrowser">Không thể mở trang Web vì thiết bị thiếu trình duyệt.</string>
<string name="mapbox_offline_error_region_definition_invalid">OfflineRegionDefinition được cung cấp không vừa thế giới: %s</string>
-
+ <string name="mapbox_telemetrySettings">Thiết lập Trình viễn trắc</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 e17f01d075..97adce8a4e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
@@ -5,6 +5,7 @@
<!--Configuration-->
<attr name="mapbox_styleUrl" format="string"/>
<attr name="mapbox_apiBaseUrl" format="string"/>
+ <attr name="mapbox_localIdeographFontFamily" format="string"/>
<!--Camera-->
<attr name="mapbox_cameraTargetLat" format="float"/>
@@ -40,6 +41,7 @@
<attr name="mapbox_myLocationBackgroundMarginBottom" format="dimension"/>
<attr name="mapbox_myLocationAccuracyTintColor" format="color"/>
<attr name="mapbox_myLocationAccuracyAlpha" format="integer"/>
+ <attr name="mapbox_myLocationAccuracyThreshold" format="float"/>
<!--Compass-->
<attr name="mapbox_uiCompass" format="boolean"/>
@@ -113,9 +115,12 @@
<attr name="mapbox_uiAttributionMarginBottom" format="dimension"/>
<attr name="mapbox_uiAttributionTintColor" format="color"/>
- <!-- Deprecated to use TextureView-->
+ <!-- Use TextureView-->
<attr name="mapbox_renderTextureMode" format="boolean"/>
+ <attr name="mapbox_enableTilePrefetch" format="boolean"/>
+ <attr name="mapbox_enableZMediaOverlay" format="boolean"/>
+
</declare-styleable>
<declare-styleable name="mapbox_BubbleLayout">
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
index 63ef42c2c3..b51c890e5c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="mapbox_gray">#7D7F80</color>
- <color name="mapbox_gray_light">#EEEEEE</color>
<color name="mapbox_blue">#1E8CAB</color>
- <color name="mapbox_my_location_ring">@color/mapbox_blue</color>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
index 8edbd47c29..1c6a265587 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
@@ -2,17 +2,8 @@
<resources>
<dimen name="mapbox_infowindow_tipview_width">8dp</dimen>
<dimen name="mapbox_infowindow_margin">8dp</dimen>
- <dimen name="mapbox_infowindow_offset">-2dp</dimen>
- <dimen name="mapbox_infowindow_line_width">1.5dp</dimen>
- <dimen name="mapbox_attribution_icon_left_padding">@dimen/mapbox_two_dp</dimen>
- <dimen name="mapbox_attribution_icon_top_padding">@dimen/mapbox_two_dp</dimen>
- <dimen name="mapbox_attribution_icon_right_padding">@dimen/mapbox_two_dp</dimen>
- <dimen name="mapbox_attribution_icon_bottom_padding">@dimen/mapbox_two_dp</dimen>
- <dimen name="mapbox_two_dp">2dp</dimen>
<dimen name="mapbox_four_dp">4dp</dimen>
<dimen name="mapbox_eight_dp">8dp</dimen>
- <dimen name="mapbox_ten_dp">10dp</dimen>
- <dimen name="mapbox_sixteen_dp">16dp</dimen>
<dimen name="mapbox_ninety_two_dp">92dp</dimen>
<dimen name="mapbox_my_locationview_outer_circle">18dp</dimen>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
index 65fb3e14a3..79c2c8d699 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
@@ -4,7 +4,7 @@
<string name="mapbox_attributionsIconContentDescription">Attribution icon. Activate to show attribution dialog.</string>
<string name="mapbox_myLocationViewContentDescription">Location View. This shows your location on the map.</string>
<string name="mapbox_mapActionDescription">Showing a Map created with Mapbox. Scroll by dragging two fingers. Zoom by pinching two fingers.</string>
- <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Maps SDK for Android</string>
<string name="mapbox_attributionTelemetryTitle">Make Mapbox Maps Better</string>
<string name="mapbox_attributionTelemetryMessage">You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.</string>
<string name="mapbox_attributionTelemetryPositive">Agree</string>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml
deleted file mode 100644
index eba1fb3a7d..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <style name="mapbox_AlertDialogStyle" parent="Theme.AppCompat.Light.Dialog.Alert"/>
-
-</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties b/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties
index bc0350fe1f..716f0ffe70 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties
+++ b/platform/android/MapboxGLAndroidSDK/src/main/resources/fabric/com.mapbox.mapboxsdk.mapbox-android-sdk.properties
@@ -1,3 +1,3 @@
fabric-identifier=com.mapbox.mapboxsdk.mapbox-android-sdk
-fabric-version=5.0.1
+fabric-version=5.1.4
fabric-build-type=binary
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 6ee5c157b9..7a28d846ea 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,6 +5,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException;
+import com.mapbox.services.android.telemetry.location.LocationEngine;
import org.junit.Before;
import org.junit.Test;
@@ -24,11 +25,13 @@ 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);
}
@@ -80,7 +83,7 @@ public class MapboxTest {
}
private void injectMapboxSingleton(String accessToken) {
- Mapbox mapbox = new Mapbox(appContext, accessToken);
+ Mapbox mapbox = new Mapbox(appContext, accessToken, locationSource);
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 8d9a360714..c3d13f0c66 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,6 +2,7 @@ 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;
@@ -74,15 +75,6 @@ public class LatLngBoundsTest {
}
@Test
- public void emptySpan() {
- latLngBounds = new LatLngBounds.Builder()
- .include(LAT_LNG_NOT_NULL_ISLAND)
- .include(LAT_LNG_NOT_NULL_ISLAND)
- .build();
- assertTrue("Should be empty", latLngBounds.isEmptySpan());
- }
-
- @Test
public void notEmptySpan() {
latLngBounds = new LatLngBounds.Builder()
.include(LAT_LNG_NOT_NULL_ISLAND)
@@ -281,4 +273,20 @@ 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);
+
+ }
}
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/AnnotationManagerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java
index 0d592f9bb3..239ad7392b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java
@@ -10,6 +10,7 @@ import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.geometry.LatLng;
import org.junit.Test;
+import org.mockito.ArgumentMatchers;
import java.util.ArrayList;
import java.util.List;
@@ -32,8 +33,9 @@ public class AnnotationManagerTest {
Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager);
Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray);
Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray);
+ ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(aNativeMapView, annotationsArray);
AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray,
- aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines);
+ aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines, shapeAnnotations);
Marker aMarker = mock(Marker.class);
long aId = 5L;
when(aNativeMapView.addMarker(aMarker)).thenReturn(aId);
@@ -58,18 +60,23 @@ public class AnnotationManagerTest {
Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager);
Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray);
Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray);
+ ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(aNativeMapView, annotationsArray);
AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray,
- aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines);
+ aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines, shapeAnnotations);
long firstId = 1L;
long secondId = 2L;
List<BaseMarkerOptions> markerList = new ArrayList<>();
MarkerOptions firstMarkerOption = new MarkerOptions().position(new LatLng()).title("first");
MarkerOptions secondMarkerOption = new MarkerOptions().position(new LatLng()).title("second");
+
markerList.add(firstMarkerOption);
markerList.add(secondMarkerOption);
MapboxMap aMapboxMap = mock(MapboxMap.class);
when(aNativeMapView.addMarker(any(Marker.class))).thenReturn(firstId, secondId);
+ when(aNativeMapView.addMarkers(ArgumentMatchers.<Marker>anyList()))
+ .thenReturn(new long[]{firstId, secondId});
+
annotationManager.addMarkers(markerList, aMapboxMap);
assertEquals(2, annotationManager.getAnnotations().size());
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java
new file mode 100644
index 0000000000..090d274fe7
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java
@@ -0,0 +1,87 @@
+package com.mapbox.mapboxsdk.maps;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class CameraChangeDispatcherTest {
+
+ @Test
+ public void testSetCameraIdleListener() {
+ CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
+ MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
+ dispatcher.setOnCameraIdleListener(listener);
+ dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ dispatcher.onCameraIdle();
+ verify(listener).onCameraIdle();
+ }
+
+ @Test
+ public void testSetCameraMoveStartedListener() {
+ CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
+ MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
+ dispatcher.setOnCameraMoveStartedListener(listener);
+ dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ }
+
+ @Test
+ public void testSetCameraMoveCancelListener() {
+ CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
+ MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
+ dispatcher.setOnCameraMoveCanceledListener(listener);
+ dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ dispatcher.onCameraMoveCanceled();
+ verify(listener).onCameraMoveCanceled();
+ }
+
+ @Test
+ public void testSetCameraMoveListener() {
+ CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
+ MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
+ dispatcher.setOnCameraMoveListener(listener);
+ dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ dispatcher.onCameraMove();
+ verify(listener).onCameraMove();
+ }
+
+ @Test
+ public void testAddCameraIdleListener() {
+ CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
+ MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
+ dispatcher.addOnCameraIdleListener(listener);
+ dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ dispatcher.onCameraIdle();
+ verify(listener).onCameraIdle();
+ }
+
+ @Test
+ public void testAddCameraMoveStartedListener() {
+ CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
+ MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
+ dispatcher.addOnCameraMoveStartedListener(listener);
+ dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ }
+
+ @Test
+ public void testAddCameraMoveCancelListener() {
+ CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
+ MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
+ dispatcher.addOnCameraMoveCancelListener(listener);
+ dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ dispatcher.onCameraMoveCanceled();
+ verify(listener).onCameraMoveCanceled();
+ }
+
+ @Test
+ public void testAddCameraMoveListener() {
+ CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
+ MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
+ dispatcher.addOnCameraMoveListener(listener);
+ dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
+ dispatcher.onCameraMove();
+ verify(listener).onCameraMove();
+ }
+}
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
new file mode 100644
index 0000000000..eeb00355bd
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java
@@ -0,0 +1,95 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.graphics.PointF;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class MapTouchListenersTest {
+
+ @Test
+ public void onMapClickListenerTest() 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.OnMapClickListener listener = mock(MapboxMap.OnMapClickListener.class);
+ mapGestureDetector.addOnMapClickListener(listener);
+ mapGestureDetector.notifyOnMapClickListeners(pointF);
+ verify(listener, times(1)).onMapClick(latLng);
+
+ mapGestureDetector.removeOnMapClickListener(listener);
+ mapGestureDetector.notifyOnMapClickListeners(pointF);
+ verify(listener, times(1)).onMapClick(latLng);
+ }
+
+ @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);
+ verify(listener, times(1)).onMapLongClick(latLng);
+
+ mapGestureDetector.removeOnMapLongClickListener(listener);
+ mapGestureDetector.notifyOnMapLongClickListeners(pointF);
+ verify(listener, times(1)).onMapLongClick(latLng);
+ }
+
+ @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();
+ verify(listener, times(1)).onFling();
+
+ mapGestureDetector.removeOnFlingListener(listener);
+ mapGestureDetector.notifyOnFlingListeners();
+ verify(listener, times(1)).onFling();
+ }
+
+ @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();
+ verify(listener, times(1)).onScroll();
+
+ mapGestureDetector.removeOnScrollListener(listener);
+ mapGestureDetector.notifyOnScrollListeners();
+ verify(listener, times(1)).onScroll();
+ }
+}
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 ce0cb00b0b..4f929641f3 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
@@ -186,5 +186,15 @@ public class MapboxMapOptionsTest {
assertEquals(Color.BLUE, new MapboxMapOptions()
.myLocationBackgroundTintColor(Color.BLUE).getMyLocationBackgroundTintColor());
}
+
+ @Test
+ public void testPrefetchesTiles() {
+ // Default value
+ assertTrue(new MapboxMapOptions().getPrefetchesTiles());
+
+ // Check mutations
+ assertTrue(new MapboxMapOptions().setPrefetchesTiles(true).getPrefetchesTiles());
+ assertFalse(new MapboxMapOptions().setPrefetchesTiles(false).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
new file mode 100644
index 0000000000..5e9f94db28
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
@@ -0,0 +1,47 @@
+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;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+
+public class MapboxMapTest {
+
+ private MapboxMap mapboxMap;
+
+ @Before
+ public void beforeTest() {
+
+ 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(AnnotationManager.class),
+ mock(CameraChangeDispatcher.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testAnimateCameraChecksDurationPositive() {
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(30.0, 30.0)),
+ 0, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testEaseCameraChecksDurationPositive() {
+ mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(new LatLng(30.0, 30.0)),
+ 0, null);
+ }
+
+ @After
+ public void afterTest() {
+ mapboxMap = null;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java
new file mode 100644
index 0000000000..eb1ce8bfaa
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java
@@ -0,0 +1,1010 @@
+package com.mapbox.mapboxsdk.style.expressions;
+
+import android.graphics.Color;
+
+import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static com.mapbox.mapboxsdk.style.expressions.Expression.acos;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.any;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.array;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.asin;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.at;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.atan;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.bool;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.coalesce;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.concat;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.cos;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.cubicBezier;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.division;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.downcase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.e;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.geometryType;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.gt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.gte;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.has;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.heatmapDensity;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.id;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.length;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.let;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.ln;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.ln2;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.log10;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.log2;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lte;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.max;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.min;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.mod;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.neq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.not;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.number;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.object;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.pi;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.pow;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.product;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.properties;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.sin;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.sqrt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.step;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.string;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.subtract;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.sum;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.tan;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toBool;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toColor;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toRgba;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.typeOf;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.upcase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.var;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * Expression unit tests that validate the expression output with the expected Object[]array representation.
+ */
+public class ExpressionTest {
+
+ @Test
+ public void testRgb() throws Exception {
+ Object[] expected = new Object[] {"rgb", 0, 0, 0};
+ Object[] actual = rgb(literal(0), literal(0), literal(0)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testRgbLiteral() throws Exception {
+ Object[] expected = new Object[] {"rgb", 0, 0, 0};
+ Object[] actual = rgb(0, 0, 0).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testRgba() throws Exception {
+ Object[] expected = new Object[] {"rgba", 0, 0, 0, 1};
+ Object[] actual = rgba(literal(0), literal(0), literal(0), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testRgbaLiteral() throws Exception {
+ Object[] expected = new Object[] {"rgba", 0, 0, 0, 1};
+ Object[] actual = rgba(0, 0, 0, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToRgba() throws Exception {
+ Object[] expected = new Object[] {"to-rgba", PropertyFactory.colorToRgbaString(Color.RED)};
+ Object[] actual = toRgba(color(Color.RED)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testEq() throws Exception {
+ Object[] expected = new Object[] {"==", 1, 1};
+ Object[] actual = eq(literal(1), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testEqLiteral() throws Exception {
+ Object[] expected = new Object[] {"==", 1, 1};
+ Object[] actual = eq(1, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNeq() throws Exception {
+ Object[] expected = new Object[] {"!=", 0, 1};
+ Object[] actual = neq(literal(0), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNeqLiteral() throws Exception {
+ Object[] expected = new Object[] {"!=", 0, 1};
+ Object[] actual = neq(0, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGt() throws Exception {
+ Object[] expected = new Object[] {">", 0, 1};
+ Object[] actual = gt(literal(0), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGtLiteral() throws Exception {
+ Object[] expected = new Object[] {">", 0, 1};
+ Object[] actual = gt(0, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLt() throws Exception {
+ Object[] expected = new Object[] {"<", 1, 0};
+ Object[] actual = lt(literal(1), literal(0)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLtLiteral() throws Exception {
+ Object[] expected = new Object[] {"<", 1, 0};
+ Object[] actual = lt(1, 0).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGte() throws Exception {
+ Object[] expected = new Object[] {">=", 1, 1};
+ Object[] actual = gte(literal(1), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGteLiteral() throws Exception {
+ Object[] expected = new Object[] {">=", 1, 1};
+ Object[] actual = gte(1, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLte() throws Exception {
+ Object[] expected = new Object[] {"<=", 1, 1};
+ Object[] actual = lte(literal(1), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLteLiteral() throws Exception {
+ Object[] expected = new Object[] {"<=", 1, 1};
+ Object[] actual = lte(1, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAll() throws Exception {
+ Object[] expected = new Object[] {"all", true, true, true};
+ Object[] actual = all(literal(true), literal(true), literal(true)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAny() throws Exception {
+ Object[] expected = new Object[] {"any", true, false, false};
+ Object[] actual = any(literal(true), literal(false), literal(false)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNot() throws Exception {
+ Object[] expected = new Object[] {"!", false};
+ Object[] actual = not(literal(false)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNotLiteral() throws Exception {
+ Object[] expected = new Object[] {"!", false};
+ Object[] actual = not(false).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSwitchCase() throws Exception {
+ Object[] expectedCaseOneGet = new Object[] {"get", "key1"};
+ Object[] expectedCaseOne = new Object[] {"==", expectedCaseOneGet, "value1"};
+ Object[] expectedCaseTwoGet = new Object[] {"get", "key2"};
+ Object[] expectedCaseTwo = new Object[] {"!=", expectedCaseTwoGet, "value2"};
+ Object[] expected = new Object[] {"case", expectedCaseOne, expectedCaseTwo};
+
+ Object[] actual = switchCase(
+ eq(get(literal("key1")), literal("value1")),
+ neq(get(literal("key2")), literal("value2"))
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSwitchCaseLiteral() throws Exception {
+ Object[] expectedCaseOneGet = new Object[] {"get", "key1"};
+ Object[] expectedCaseOne = new Object[] {"==", expectedCaseOneGet, "value1"};
+ Object[] expectedCaseTwoGet = new Object[] {"get", "key2"};
+ Object[] expectedCaseTwo = new Object[] {"!=", expectedCaseTwoGet, "value2"};
+ Object[] expected = new Object[] {"case", expectedCaseOne, expectedCaseTwo};
+
+ Object[] actual = switchCase(
+ eq(get("key1"), literal("value1")),
+ neq(get("key2"), literal("value2"))
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMatch() throws Exception {
+ Object[] labelZero = new Object[] {"a", "output"};
+ Object[] labelOne = new Object[] {"b", "output2"};
+ Object[] labelTwo = new Object[] {"c", "output3"};
+
+ Object[] expected = new Object[] {"match", labelZero, labelOne, labelTwo};
+ Object[] actual = match(literal(labelZero), literal(labelOne), literal(labelTwo)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCoalesce() throws Exception {
+ Object[] expectedGetOne = new Object[] {"get", "invalidKey"};
+ Object[] expectedGetTwo = new Object[] {"get", "validKey"};
+ Object[] expected = new Object[] {"coalesce", expectedGetOne, expectedGetTwo};
+
+ Object[] actual = coalesce(
+ get(literal("invalidKey")),
+ get(literal("validKey"))
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCoalesceLiteral() throws Exception {
+ Object[] expectedGetOne = new Object[] {"get", "invalidKey"};
+ Object[] expectedGetTwo = new Object[] {"get", "validKey"};
+ Object[] expected = new Object[] {"coalesce", expectedGetOne, expectedGetTwo};
+
+ Object[] actual = coalesce(
+ get("invalidKey"),
+ get("validKey")
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testProperties() throws Exception {
+ Object[] expected = new Object[] {"properties"};
+ Object[] actual = properties().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGeometryType() throws Exception {
+ Object[] expected = new Object[] {"geometry-type"};
+ Object[] actual = geometryType().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testId() throws Exception {
+ Object[] expected = new Object[] {"id"};
+ Object[] actual = id().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHeatmapDensity() throws Exception {
+ Object[] expected = new Object[] {"heatmap-density"};
+ Object[] actual = heatmapDensity().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAt() throws Exception {
+ Object[] expected = new Object[] {"at", 3, new Object[] {"one", "two"}};
+ Object[] actual = at(literal(3), literal(new Object[] {"one", "two"})).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAtLiteral() throws Exception {
+ Object[] expected = new Object[] {"at", 3, new Object[] {"one", "two"}};
+ Object[] actual = at(3, literal(new Object[] {"one", "two"})).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAtExpression() throws Exception {
+ Object[] expected = new Object[] {"at", 3, new Object[] {"properties"}};
+ Object[] actual = at(literal(3), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGet() throws Exception {
+ Object[] expected = new Object[] {"get", "key"};
+ Object[] actual = get(literal("key")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGetLiteral() throws Exception {
+ Object[] expected = new Object[] {"get", "key"};
+ Object[] actual = get("key").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGetObject() throws Exception {
+ Object[] expected = new Object[] {"get", "key", new Object[] {"properties"}};
+ Object[] actual = get(literal("key"), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testGetObjectLiteral() throws Exception {
+ Object[] expected = new Object[] {"get", "key", new Object[] {"properties"}};
+ Object[] actual = get("key", properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHas() throws Exception {
+ Object[] expected = new Object[] {"has", "key"};
+ Object[] actual = has(literal("key")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasLiteral() throws Exception {
+ Object[] expected = new Object[] {"has", "key"};
+ Object[] actual = has("key").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasObject() throws Exception {
+ Object[] expected = new Object[] {"has", "key", new Object[] {"properties"}};
+ Object[] actual = has(literal("key"), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasObjectLiteral() throws Exception {
+ Object[] expected = new Object[] {"has", "key", new Object[] {"properties"}};
+ Object[] actual = has("key", properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasExpression() throws Exception {
+ Object[] expected = new Object[] {"has", new Object[] {"get", "key"}, new Object[] {"properties"}};
+ Object[] actual = has(get(literal("key")), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testHasExpressionLiteral() throws Exception {
+ Object[] expected = new Object[] {"has", new Object[] {"get", "key"}, new Object[] {"properties"}};
+ Object[] actual = has(get("key"), properties()).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLength() throws Exception {
+ Object[] expected = new Object[] {"length", "key"};
+ Object[] actual = length(literal("key")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLengthLiteral() throws Exception {
+ Object[] expected = new Object[] {"length", "key"};
+ Object[] actual = length("key").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLengthExpression() throws Exception {
+ Object[] expected = new Object[] {"length", new Object[] {"get", "key"}};
+ Object[] actual = length(get(literal("key"))).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLn2() throws Exception {
+ Object[] expected = new Object[] {"ln2"};
+ Object[] actual = ln2().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testPi() throws Exception {
+ Object[] expected = new Object[] {"pi"};
+ Object[] actual = pi().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testE() throws Exception {
+ Object[] expected = new Object[] {"e"};
+ Object[] actual = e().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSum() throws Exception {
+ Object[] expected = new Object[] {"+", 1, 2};
+ Object[] actual = sum(literal(1), literal(2)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSumLiteral() throws Exception {
+ Object[] expected = new Object[] {"+", 1, 2};
+ Object[] actual = sum(1, 2).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testProduct() throws Exception {
+ Object[] expected = new Object[] {"*", 1, 2};
+ Object[] actual = product(literal(1), literal(2)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testProductLiteral() throws Exception {
+ Object[] expected = new Object[] {"*", 1, 2};
+ Object[] actual = product(1, 2).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSubtract() throws Exception {
+ Object[] expected = new Object[] {"-", 2, 1};
+ Object[] actual = subtract(literal(2), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSubtractLiteral() throws Exception {
+ Object[] expected = new Object[] {"-", 2, 1};
+ Object[] actual = subtract(2, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDivision() throws Exception {
+ Object[] expected = new Object[] {"/", 2, 1};
+ Object[] actual = division(literal(2), literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDivisionLiteral() throws Exception {
+ Object[] expected = new Object[] {"/", 2, 1};
+ Object[] actual = division(2, 1).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDivisionWithNestedGet() throws Exception {
+ Object nestedGet = new Object[] {"get", "key"};
+ Object[] expected = new Object[] {"/", 2, nestedGet};
+ Object[] actual = division(literal(2), get(literal("key"))).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMod() throws Exception {
+ Object[] expected = new Object[] {"%", 1, 3};
+ Object[] actual = mod(literal(1), literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testModLiteral() throws Exception {
+ Object[] expected = new Object[] {"%", 1, 3};
+ Object[] actual = mod(1, 3).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testPow() throws Exception {
+ Object[] expected = new Object[] {"^", 2, 3};
+ Object[] actual = pow(literal(2), literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testPowLiteral() throws Exception {
+ Object[] expected = new Object[] {"^", 2, 3};
+ Object[] actual = pow(2, 3).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSqrt() throws Exception {
+ Object[] expected = new Object[] {"sqrt", 4};
+ Object[] actual = sqrt(literal(4)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSqrtLiteral() throws Exception {
+ Object[] expected = new Object[] {"sqrt", 4};
+ Object[] actual = sqrt(4).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLog10() throws Exception {
+ Object[] expected = new Object[] {"log10", 10};
+ Object[] actual = log10(literal(10)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLog10Literal() throws Exception {
+ Object[] expected = new Object[] {"log10", 10};
+ Object[] actual = log10(10).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLn() throws Exception {
+ Object[] expected = new Object[] {"ln", 2};
+ Object[] actual = ln(literal(2)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLnLiteral() throws Exception {
+ Object[] expected = new Object[] {"ln", 2};
+ Object[] actual = ln(2).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLog2() throws Exception {
+ Object[] expected = new Object[] {"log2", 16};
+ Object[] actual = log2(literal(16)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLog2Literal() throws Exception {
+ Object[] expected = new Object[] {"log2", 16};
+ Object[] actual = log2(16).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSin() throws Exception {
+ Object[] expected = new Object[] {"sin", 45};
+ Object[] actual = sin(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testSinLiteral() throws Exception {
+ Object[] expected = new Object[] {"sin", 45};
+ Object[] actual = sin(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCos() throws Exception {
+ Object[] expected = new Object[] {"cos", 45};
+ Object[] actual = cos(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCosLiteral() throws Exception {
+ Object[] expected = new Object[] {"cos", 45};
+ Object[] actual = cos(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testTan() throws Exception {
+ Object[] expected = new Object[] {"tan", 45};
+ Object[] actual = tan(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testTanLiteral() throws Exception {
+ Object[] expected = new Object[] {"tan", 45};
+ Object[] actual = tan(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAsin() throws Exception {
+ Object[] expected = new Object[] {"asin", 45};
+ Object[] actual = asin(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAsinLiteral() throws Exception {
+ Object[] expected = new Object[] {"asin", 45};
+ Object[] actual = asin(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAcos() throws Exception {
+ Object[] expected = new Object[] {"acos", 45};
+ Object[] actual = acos(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAcosLiteral() throws Exception {
+ Object[] expected = new Object[] {"acos", 45};
+ Object[] actual = acos(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAtan() throws Exception {
+ Object[] expected = new Object[] {"atan", 45};
+ Object[] actual = atan(literal(45)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAtanLiteral() throws Exception {
+ Object[] expected = new Object[] {"atan", 45};
+ Object[] actual = atan(45).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMin() throws Exception {
+ Object[] expected = new Object[] {"min", 0, 1, 2, 3};
+ Object[] actual = min(literal(0), literal(1), literal(2), literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMinLiteral() throws Exception {
+ Object[] expected = new Object[] {"min", 0, 1, 2, 3};
+ Object[] actual = min(0, 1, 2, 3).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMax() throws Exception {
+ Object[] expected = new Object[] {"max", 0, 1, 2, 3};
+ Object[] actual = max(literal(0), literal(1), literal(2), literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMaxLiteral() throws Exception {
+ Object[] expected = new Object[] {"max", 0, 1, 2, 3};
+ Object[] actual = max(0, 1, 2, 3).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testUpcase() throws Exception {
+ Object[] expected = new Object[] {"upcase", "string"};
+ Object[] actual = upcase(literal("string")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testUpcaseLiteral() throws Exception {
+ Object[] expected = new Object[] {"upcase", "string"};
+ Object[] actual = upcase("string").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDowncase() throws Exception {
+ Object[] expected = new Object[] {"downcase", "string"};
+ Object[] actual = downcase(literal("string")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testDowncaseLiteral() throws Exception {
+ Object[] expected = new Object[] {"downcase", "string"};
+ Object[] actual = downcase("string").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testConcat() throws Exception {
+ Object[] expected = new Object[] {"concat", "foo", "bar"};
+ Object[] actual = concat(literal("foo"), literal("bar")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testConcatLiteral() throws Exception {
+ Object[] expected = new Object[] {"concat", "foo", "bar"};
+ Object[] actual = concat("foo", "bar").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testArray() throws Exception {
+ Object[] get = new Object[] {"get", "keyToArray"};
+ Object[] expected = new Object[] {"array", get};
+ Object[] actual = array(get(literal("keyToArray"))).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testArrayLiteral() throws Exception {
+ Object[] get = new Object[] {"get", "keyToArray"};
+ Object[] expected = new Object[] {"array", get};
+ Object[] actual = array(get("keyToArray")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testTypeOf() throws Exception {
+ Object[] expected = new Object[] {"typeof", "value"};
+ Object[] actual = typeOf(literal("value")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testString() throws Exception {
+ Object[] expected = new Object[] {"string", "value"};
+ Object[] actual = string(literal("value")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testNumber() throws Exception {
+ Object[] expected = new Object[] {"number", 1};
+ Object[] actual = number(literal(1)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testBool() throws Exception {
+ Object[] expected = new Object[] {"boolean", true};
+ Object[] actual = bool(literal(true)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testObject() throws Exception {
+ Object object = new Object();
+ Object[] expected = new Object[] {"object", object};
+ Object[] actual = object(literal(object)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToString() throws Exception {
+ Object[] expected = new Object[] {"to-string", 3};
+ Object[] actual = Expression.toString(literal(3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToNumber() throws Exception {
+ Object[] expected = new Object[] {"to-number", "3"};
+ Object[] actual = toNumber(literal("3")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToBool() throws Exception {
+ Object[] expected = new Object[] {"to-boolean", "true"};
+ Object[] actual = toBool(literal("true")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testToColor() throws Exception {
+ Object[] expected = new Object[] {"to-color", "value"};
+ Object[] actual = toColor(literal("value")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLet() throws Exception {
+ Object[] expected = new Object[] {"let", "letName", "value"};
+ Object[] actual = let(literal("letName"), literal("value")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testVar() throws Exception {
+ Object[] expected = new Object[] {"var", "letName"};
+ Object[] actual = var(literal("letName")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testVarLiteral() throws Exception {
+ Object[] expected = new Object[] {"var", "letName"};
+ Object[] actual = var("letName").toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testVarExpression() throws Exception {
+ Object[] expected = new Object[] {"var", new Object[] {"get", "letName"}};
+ Object[] actual = var(get(literal("letName"))).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testVarExpressionLiteral() throws Exception {
+ Object[] expected = new Object[] {"var", new Object[] {"get", "letName"}};
+ Object[] actual = var(get("letName")).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testZoom() throws Exception {
+ Object[] expected = new Object[] {"zoom"};
+ Object[] actual = zoom().toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testStepBasic() throws Exception {
+ Object[] expected = new Object[] {"step", 12, 11, 0, 111, 1, 1111};
+ Object[] actual = step(literal(12), literal(11), literal(0), literal(111), literal(1), literal(1111)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testStepBasicLiteral() throws Exception {
+ Object[] expected = new Object[] {"step", new Object[] {"get", "line-width"}, 11, 0, 111, 1, 1111};
+ Object[] actual = step(get("line-width"), literal(11), stop(0, 111), stop(1, 1111)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testStepExpression() throws Exception {
+ Object[] input = new Object[] {"get", "key"};
+ Object[] number = new Object[] {"to-number", input};
+ Object[] expected = new Object[] {"step", number, 11, 0, 111, 1, 1111};
+ Object[] actual = step(toNumber(get(literal("key"))),
+ literal(11), literal(0), literal(111), literal(1), literal(1111)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testStepExpressionLiteral() throws Exception {
+ Object[] input = new Object[] {"get", "key"};
+ Object[] number = new Object[] {"to-number", input};
+ Object[] expected = new Object[] {"step", number, 11, 0, 111, 1, 1111};
+ Object[] actual = step(toNumber(get("key")), literal(11), stop(0, 111), stop(1, 1111)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLinear() throws Exception {
+ Object[] stopZero = new Object[] {0, 1};
+ Object[] stopOne = new Object[] {1, 2};
+ Object[] stopTwo = new Object[] {2, 3};
+ Object[] expected = new Object[] {"interpolate", new Object[] {"linear"}, 12, stopZero, stopOne, stopTwo};
+ Object[] actual = interpolate(linear(), literal(12),
+ literal(stopZero), literal(stopOne), literal(stopTwo)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLinearStops() throws Exception {
+ Object[] expected = new Object[] {"interpolate", new Object[] {"linear"}, 12, 0, 1, 1, 2, 2, 3};
+ Object[] actual = interpolate(linear(), literal(12), stop(0, 1), stop(1, 2), stop(2, 3)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testExponential() throws Exception {
+ Object[] exponential = new Object[] {"exponential", 12};
+ Object[] get = new Object[] {"get", "x"};
+ Object[] expected = new Object[] {"interpolate", exponential, get, 0, 100, 200};
+ Object[] actual = interpolate(exponential(literal(12)),
+ get(literal("x")), literal(0), literal(100), literal(200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testExponentialLiteral() throws Exception {
+ Object[] exponential = new Object[] {"exponential", 12};
+ Object[] get = new Object[] {"get", "x"};
+ Object[] expected = new Object[] {"interpolate", exponential, get, 0, 100, 100, 200};
+ Object[] actual = interpolate(exponential(12), get("x"), stop(0, 100), stop(100, 200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testExponentialExpressionLiteral() throws Exception {
+ Object[] getX = new Object[] {"get", "x"};
+ Object[] exponential = new Object[] {"exponential", getX};
+ Object[] getY = new Object[] {"get", "y"};
+ Object[] expected = new Object[] {"interpolate", exponential, getY, 0, 100, 100, 200};
+ Object[] actual = interpolate(exponential(get("x")), get("y"), stop(0, 100), stop(100, 200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCubicBezier() throws Exception {
+ Object[] cubicBezier = new Object[] {"cubic-bezier", 1, 1, 1, 1};
+ Object[] get = new Object[] {"get", "x"};
+ Object[] expected = new Object[] {"interpolate", cubicBezier, get, 0, 100, 100, 200};
+ Object[] actual = interpolate(cubicBezier(literal(1), literal(1), literal(1), literal(1)),
+ get(literal("x")), literal(0), literal(100), literal(100), literal(200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCubicBezierLiteral() throws Exception {
+ Object[] cubicBezier = new Object[] {"cubic-bezier", 1, 1, 1, 1};
+ Object[] get = new Object[] {"get", "x"};
+ Object[] expected = new Object[] {"interpolate", cubicBezier, get, 0, 100, 100, 200};
+ Object[] actual = interpolate(cubicBezier(1, 1, 1, 1), get("x"), stop(0, 100), stop(100, 200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCubicBezierExpression() throws Exception {
+ Object[] getX = new Object[] {"get", "x"};
+ Object[] getY = new Object[] {"get", "y"};
+ Object[] getZ = new Object[] {"get", "z"};
+ Object[] cubicBezier = new Object[] {"cubic-bezier", getZ, 1, getY, 1};
+ Object[] expected = new Object[] {"interpolate", cubicBezier, getX, 0, 100, 200};
+ Object[] actual = interpolate(cubicBezier(get(literal("z")), literal(1),
+ get(literal("y")), literal(1)), get(literal("x")), literal(0), literal(100), literal(200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCubicBezierExpressionLiteral() throws Exception {
+ Object[] getX = new Object[] {"get", "x"};
+ Object[] getY = new Object[] {"get", "y"};
+ Object[] getZ = new Object[] {"get", "z"};
+ Object[] cubicBezier = new Object[] {"cubic-bezier", getZ, 1, getY, 1};
+ Object[] expected = new Object[] {"interpolate", cubicBezier, getX, 0, 100, 100, 200};
+ Object[] actual = interpolate(cubicBezier(get("z"), literal(1), get("y"),
+ literal(1)), get("x"), stop(0, 100), stop(100, 200)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/.gitignore b/platform/android/MapboxGLAndroidSDKTestApp/.gitignore
new file mode 100644
index 0000000000..cec211fe81
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/.gitignore
@@ -0,0 +1,2 @@
+lint-baseline.xml
+lint/lint-baseline-local.xml \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
index 656789fcdb..765b346669 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -14,8 +14,8 @@ android {
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
@@ -25,11 +25,11 @@ android {
}
lintOptions {
+ baseline file("lint-baseline-local.xml")
checkAllWarnings true
warningsAsErrors true
- disable 'MissingTranslation'
- disable 'IconDensities'
- disable 'InvalidPackage'
+ disable 'MissingTranslation', 'GoogleAppIndexingWarning', 'UnpackedNativeCode', 'IconDipSize', 'TypographyQuotes'
+ abortOnError false
}
buildTypes {
@@ -52,37 +52,37 @@ android {
}
dependencies {
- compile(project(':MapboxGLAndroidSDK')) {
- transitive = true
- }
+ implementation(project(':MapboxGLAndroidSDK'))
// Support libraries
- compile rootProject.ext.dep.supportAppcompatV7
- compile rootProject.ext.dep.supportRecyclerView
+ implementation rootProject.ext.dep.supportAppcompatV7
+ implementation rootProject.ext.dep.supportRecyclerView
+ implementation rootProject.ext.dep.supportDesign
// Leak Canary
- debugCompile rootProject.ext.dep.leakCanaryDebug
- releaseCompile rootProject.ext.dep.leakCanaryRelease
- testCompile rootProject.ext.dep.leakCanaryTest
+ debugImplementation rootProject.ext.dep.leakCanaryDebug
+ releaseImplementation rootProject.ext.dep.leakCanaryRelease
+
+ // Timber
+ implementation rootProject.ext.dep.timber
// Mapbox Android Services (Java component)
- compile(rootProject.ext.dep.mapboxJavaServices) {
+ implementation(rootProject.ext.dep.mapboxJavaServices) {
transitive = true
}
+ implementation rootProject.ext.dep.lost
// Testing dependencies
- androidTestCompile rootProject.ext.dep.testSpoonRunner
- 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 rootProject.ext.dep.supportAnnotations
+ androidTestImplementation rootProject.ext.dep.testRunner
+ androidTestImplementation rootProject.ext.dep.testRules
+ androidTestImplementation rootProject.ext.dep.testEspressoCore
+ androidTestImplementation rootProject.ext.dep.testEspressoIntents
}
apply from: 'gradle-make.gradle'
apply from: 'gradle-config.gradle'
-apply from: 'gradle-device-farm.gradle'
-apply from: 'gradle-spoon.gradle'
apply from: 'gradle-checkstyle.gradle'
+apply from: '../gradle-lint.gradle'
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle b/platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle
index fdc4399f16..e4e1ba0453 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle
@@ -13,6 +13,7 @@ task checkstyle(type: Checkstyle) {
include '**/*.java'
exclude '**/gen/**'
exclude '**/style/*LayerTest.java'
+ exclude '**/style/LightTest.java'
classpath = files()
ignoreFailures = false
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/gradle-device-farm.gradle b/platform/android/MapboxGLAndroidSDKTestApp/gradle-device-farm.gradle
deleted file mode 100644
index 5cb5d75bbb..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/gradle-device-farm.gradle
+++ /dev/null
@@ -1,43 +0,0 @@
-apply plugin: 'devicefarm'
-
-def getAccessKeyDeviceFarm() {
- if (project.hasProperty('AWS_ACCESS_KEY_ID_DEVICE_FARM')) {
- return AWS_ACCESS_KEY_ID_DEVICE_FARM
- } else {
- println("Could not locate AWS_ACCESS_KEY_ID_DEVICE_FARM in gradle.properties")
- return ""
- }
-}
-
-def getSecretAccessKeyDeviceFarm() {
- if (project.hasProperty('AWS_SECRET_ACCESS_KEY_DEVICE_FARM')) {
- return AWS_SECRET_ACCESS_KEY_DEVICE_FARM
- } else {
- println("Could not locate AWS_SECRET_ACCESS_KEY_DEVICE_FARM in gradle.properties")
- return ""
- }
-}
-
-devicefarm {
-
- projectName "Mapbox GL Android" // required: Must already exists.
- devicePool "sanity" // optional: Defaults to "Top Devices"
-
- authentication {
- accessKey getAccessKeyDeviceFarm()
- secretKey getSecretAccessKeyDeviceFarm()
- }
-
- devicestate {
- wifi "on"
- bluetooth "off"
- gps "on"
- nfc "on"
- latitude 47.6204 // default
- longitude - 122.3491 // default
- }
-
- instrumentation {
-
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/gradle-spoon.gradle b/platform/android/MapboxGLAndroidSDKTestApp/gradle-spoon.gradle
deleted file mode 100644
index 7a5b966443..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/gradle-spoon.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-println "configuring spoon"
-apply plugin: 'spoon'
-spoon {
- // Spoon: distributes instrumentation tests to all your Androids
- // for more options see https://github.com/stanfy/spoon-gradle-plugin
- grantAllPermissions = true
- debug = true
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml b/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml
new file mode 100644
index 0000000000..e3c5abce4f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 2.3.1">
+
+ <issue
+ id="UnusedResources"
+ message="The resource `R.string.mapbox_access_token` appears to be unused"
+ errorLine1=" &lt;string name=&quot;mapbox_access_token&quot;>YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE&lt;/string>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/values/developer-config.xml"
+ line="3"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `#cccc` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
+ errorLine1=" android:background=&quot;#cccc&quot;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/layout/drawer_navigation_drawer.xml"
+ line="4"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `?android:attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
+ errorLine1=" android:background=&quot;?android:attr/selectableItemBackground&quot;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/layout/item_main_feature.xml"
+ line="6"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `@color/mapboxGreen` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"
+ errorLine1=" android:background=&quot;@color/mapboxGreen&quot;>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/res/layout/view_text_marker.xml"
+ line="5"
+ column="5"/>
+ </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>
+
+ <issue
+ id="IconDuplicatesConfig"
+ message="The `icon_burned.png` icon has identical contents in the following configuration folders: drawable-hdpi, drawable-xhdpi">
+ <location
+ file="src/main/res/drawable-xhdpi/icon_burned.png"/>
+ <location
+ file="src/main/res/drawable-hdpi/icon_burned.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-hdpi/ic_launcher_round.png`: expected 72x72, but was 216x216">
+ <location
+ file="src/main/res/drawable-hdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-mdpi/ic_launcher_round.png`: expected 48x48, but was 144x144">
+ <location
+ file="src/main/res/drawable-mdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xhdpi/ic_launcher_round.png`: expected 96x96, but was 288x288">
+ <location
+ file="src/main/res/drawable-xhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxhdpi/ic_launcher_round.png`: expected 144x144, but was 432x432">
+ <location
+ file="src/main/res/drawable-xxhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxxhdpi/ic_launcher_round.png`: expected 192x192, but was 576x576">
+ <location
+ file="src/main/res/drawable-xxxhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-hdpi/icon.png`: expected 72x72, but was 215x212">
+ <location
+ file="src/main/res/drawable-hdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-mdpi/icon.png`: expected 48x48, but was 143x141">
+ <location
+ file="src/main/res/drawable-mdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xhdpi/icon.png`: expected 96x96, but was 286x282">
+ <location
+ file="src/main/res/drawable-xhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxhdpi/icon.png`: expected 144x144, but was 429x423">
+ <location
+ file="src/main/res/drawable-xxhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxxhdpi/icon.png`: expected 192x192, but was 572x564">
+ <location
+ file="src/main/res/drawable-xxxhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-hdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)">
+ <location
+ file="src/main/res/drawable-hdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-mdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png">
+ <location
+ file="src/main/res/drawable-mdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-xhdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)">
+ <location
+ file="src/main/res/drawable-xhdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-xxhdpi`: ic_car_top.png, ic_taxi_top.png, southeast_radar_0.png, southeast_radar_1.png, southeast_radar_2.png... (1 more)">
+ <location
+ file="src/main/res/drawable-xxhdpi"/>
+ </issue>
+
+</issues>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml b/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml
new file mode 100644
index 0000000000..64e3d41bcc
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml
@@ -0,0 +1,177 @@
+<?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 by="lint 2.3.1" format="4">
+
+ <issue
+ errorLine1=" android:background=&quot;#cccc&quot;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `#cccc` with a theme that also paints a background (inferred theme is `@style/AppTheme`)">
+ <location
+ column="5"
+ file="src/main/res/layout/drawer_navigation_drawer.xml"
+ line="4"/>
+ </issue>
+
+ <issue
+ errorLine1=" android:background=&quot;?android:attr/selectableItemBackground&quot;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `?android:attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/AppTheme`)">
+ <location
+ column="5"
+ file="src/main/res/layout/item_main_feature.xml"
+ line="6"/>
+ </issue>
+
+ <issue
+ errorLine1=" android:background=&quot;@color/mapboxGreen&quot;>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
+ id="Overdraw"
+ message="Possible overdraw: Root element paints background `@color/mapboxGreen` with a theme that also paints a background (inferred theme is `@style/AppTheme`)">
+ <location
+ column="5"
+ file="src/main/res/layout/view_text_marker.xml"
+ line="5"/>
+ </issue>
+
+ <issue
+ 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=" ^"
+ id="TypographyQuotes"
+ message="Replace straight quotes (&apos;&apos;) with directional quotes (‘’, &amp;#8216; and &amp;#8217;) ?">
+ <location
+ column="55"
+ file="src/main/res/values-ca/strings.xml"
+ line="9"/>
+ </issue>
+
+ <issue
+ id="IconDipSize"
+ message="The image `icon_burned.png` varies significantly in its density-independent (dip) size across the various density versions: drawable-hdpi/icon_burned.png: 64x64 dp (96x96 px), drawable-xxxhdpi/icon_burned.png: 48x48 dp (192x192 px), drawable-xxhdpi/icon_burned.png: 48x48 dp (144x144 px), drawable-xhdpi/icon_burned.png: 48x48 dp (96x96 px), drawable-mdpi/icon_burned.png: 48x48 dp (48x48 px)">
+ <location
+ file="src/main/res/drawable-mdpi/icon_burned.png"/>
+ <location
+ file="src/main/res/drawable-xhdpi/icon_burned.png"/>
+ <location
+ file="src/main/res/drawable-xxhdpi/icon_burned.png"/>
+ <location
+ file="src/main/res/drawable-xxxhdpi/icon_burned.png"/>
+ <location
+ file="src/main/res/drawable-hdpi/icon_burned.png"/>
+ </issue>
+
+ <issue
+ id="IconDuplicatesConfig"
+ message="The `icon_burned.png` icon has identical contents in the following configuration folders: drawable-hdpi, drawable-xhdpi">
+ <location
+ file="src/main/res/drawable-xhdpi/icon_burned.png"/>
+ <location
+ file="src/main/res/drawable-hdpi/icon_burned.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-hdpi/ic_launcher_round.png`: expected 72x72, but was 216x216">
+ <location
+ file="src/main/res/drawable-hdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-mdpi/ic_launcher_round.png`: expected 48x48, but was 144x144">
+ <location
+ file="src/main/res/drawable-mdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xhdpi/ic_launcher_round.png`: expected 96x96, but was 288x288">
+ <location
+ file="src/main/res/drawable-xhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxhdpi/ic_launcher_round.png`: expected 144x144, but was 432x432">
+ <location
+ file="src/main/res/drawable-xxhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxxhdpi/ic_launcher_round.png`: expected 192x192, but was 576x576">
+ <location
+ file="src/main/res/drawable-xxxhdpi/ic_launcher_round.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-hdpi/icon.png`: expected 72x72, but was 215x212">
+ <location
+ file="src/main/res/drawable-hdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-mdpi/icon.png`: expected 48x48, but was 143x141">
+ <location
+ file="src/main/res/drawable-mdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xhdpi/icon.png`: expected 96x96, but was 286x282">
+ <location
+ file="src/main/res/drawable-xhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxhdpi/icon.png`: expected 144x144, but was 429x423">
+ <location
+ file="src/main/res/drawable-xxhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconExpectedSize"
+ message="Incorrect icon size for `drawable-xxxhdpi/icon.png`: expected 192x192, but was 572x564">
+ <location
+ file="src/main/res/drawable-xxxhdpi/icon.png"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-hdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)">
+ <location
+ file="src/main/res/drawable-hdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-mdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png">
+ <location
+ file="src/main/res/drawable-mdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-xhdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)">
+ <location
+ file="src/main/res/drawable-xhdpi"/>
+ </issue>
+
+ <issue
+ id="IconDensities"
+ message="Missing the following drawables in `drawable-xxhdpi`: ic_car_top.png, ic_taxi_top.png, southeast_radar_0.png, southeast_radar_1.png, southeast_radar_2.png... (1 more)">
+ <location
+ file="src/main/res/drawable-xxhdpi"/>
+ </issue>
+
+</issues>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java
new file mode 100644
index 0000000000..3e226a7ec5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java
@@ -0,0 +1,42 @@
+package com.mapbox.mapboxsdk.maps;
+
+import com.mapbox.mapboxsdk.annotations.Icon;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+import timber.log.Timber;
+
+public class IconManagerResolver {
+
+ private IconManager iconManager;
+
+ public IconManagerResolver(MapboxMap mapboxMap) {
+ try {
+ Field annotationManagerField = MapboxMap.class.getDeclaredField("annotationManager");
+ annotationManagerField.setAccessible(true);
+ AnnotationManager annotationManager = (AnnotationManager) annotationManagerField.get(mapboxMap);
+
+ Field iconManagerField = AnnotationManager.class.getDeclaredField("iconManager");
+ iconManagerField.setAccessible(true);
+ iconManager = (IconManager) iconManagerField.get(annotationManager);
+ } catch (Exception exception) {
+ Timber.e(exception, "Could not create IconManagerResolver, unable to reflect.");
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<Icon, Integer> getIconMap() {
+ try {
+ Field field = IconManager.class.getDeclaredField("iconMap");
+ field.setAccessible(true);
+ return (Map<Icon, Integer>) field.get(iconManager);
+ } catch (NoSuchFieldException exception) {
+ Timber.e(exception, "Could not getIconMap, unable to reflect.");
+ } catch (IllegalAccessException exception) {
+ Timber.e(exception, "Could not getIconMap, unable to reflect.");
+ }
+ return new HashMap<>();
+ }
+}
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 a813b7f368..b4bc118129 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
@@ -1,8 +1,6 @@
package com.mapbox.mapboxsdk.maps;
import android.graphics.Color;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.view.View;
@@ -14,6 +12,7 @@ import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -31,9 +30,12 @@ import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
+import timber.log.Timber;
+
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.testapp.utils.TestConstants.LAT_LNG_DELTA;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertEquals;
@@ -66,44 +68,61 @@ 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
+ );
}));
}
//
- // CameraForLatLngBounds
+ // Camera tests
//
@Test
+ public void testCameraPositionOnFinish() {
+ ViewUtils.checkViewIsDisplayed(R.id.mapView);
+ 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() {
+ }
+
+ @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);
}));
}
@@ -114,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);
}));
}
@@ -127,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);
}));
}
@@ -140,14 +153,11 @@ public class MapboxMapTest extends BaseActivityTest {
@Ignore
public void testInitialZoomLevels() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- assertEquals("MaxZoom should match", MapboxConstants.MAXIMUM_ZOOM, mapboxMap.getMaxZoomLevel(),
- TestConstants.ZOOM_DELTA);
- assertEquals("MinZoom should match", MapboxConstants.MINIMUM_ZOOM, mapboxMap.getMinZoomLevel(),
- TestConstants.ZOOM_DELTA);
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ assertEquals("MaxZoom should match", MapboxConstants.MAXIMUM_ZOOM, mapboxMap.getMaxZoomLevel(),
+ TestConstants.ZOOM_DELTA);
+ assertEquals("MinZoom should match", MapboxConstants.MINIMUM_ZOOM, mapboxMap.getMinZoomLevel(),
+ TestConstants.ZOOM_DELTA);
}));
}
@@ -168,43 +178,28 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testConcurrentInfoWindowEnabled() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(true);
- assertTrue("ConcurrentWindows should be true", mapboxMap.isAllowConcurrentMultipleOpenInfoWindows());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(true);
+ assertTrue("ConcurrentWindows should be true", mapboxMap.isAllowConcurrentMultipleOpenInfoWindows());
}));
}
@Test
public void testConcurrentInfoWindowDisabled() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(false);
- assertFalse("ConcurrentWindows should be false", mapboxMap.isAllowConcurrentMultipleOpenInfoWindows());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(false);
+ assertFalse("ConcurrentWindows should be false", mapboxMap.isAllowConcurrentMultipleOpenInfoWindows());
}));
}
@Test
public void testInfoWindowAdapter() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.InfoWindowAdapter infoWindowAdapter = new MapboxMap.InfoWindowAdapter() {
- @Nullable
- @Override
- public View getInfoWindow(@NonNull Marker marker) {
- return null;
- }
- };
- mapboxMap.setInfoWindowAdapter(infoWindowAdapter);
- assertEquals("InfoWindowAdpter should be the same", infoWindowAdapter, mapboxMap.getInfoWindowAdapter());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.InfoWindowAdapter infoWindowAdapter = marker -> null;
+ mapboxMap.setInfoWindowAdapter(infoWindowAdapter);
+ assertEquals("InfoWindowAdpter should be the same", infoWindowAdapter, mapboxMap.getInfoWindowAdapter());
}));
}
@@ -216,12 +211,9 @@ public class MapboxMapTest extends BaseActivityTest {
@Ignore /* disabled due to enabling permissions during test #7177 */
public void testMyLocationEnabled() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setMyLocationEnabled(true);
- assertTrue("MyLocationEnabled should be true", mapboxMap.isMyLocationEnabled());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setMyLocationEnabled(true);
+ assertTrue("MyLocationEnabled should be true", mapboxMap.isMyLocationEnabled());
}));
}
@@ -229,12 +221,9 @@ public class MapboxMapTest extends BaseActivityTest {
@Ignore /* can't create handler inside thread that not called Looper.prepare() */
public void testMyLocationDisabled() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- mapboxMap.setMyLocationEnabled(false);
- assertFalse("MyLocationEnabled should be false", mapboxMap.isMyLocationEnabled());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setMyLocationEnabled(false);
+ assertFalse("MyLocationEnabled should be false", mapboxMap.isMyLocationEnabled());
}));
}
@@ -245,73 +234,49 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testFpsListener() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.OnFpsChangedListener fpsChangedListener = new MapboxMap.OnFpsChangedListener() {
- @Override
- public void onFpsChanged(double fps) {
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.OnFpsChangedListener fpsChangedListener = fps -> {
- }
- };
- mapboxMap.setOnFpsChangedListener(fpsChangedListener);
- assertEquals("FpsListener should match", fpsChangedListener, mapboxMap.getOnFpsChangedListener());
- }
+ };
+ mapboxMap.setOnFpsChangedListener(fpsChangedListener);
+ assertEquals("FpsListener should match", fpsChangedListener, mapboxMap.getOnFpsChangedListener());
}));
}
@Test
public void testInfoWindowClickListener() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.OnInfoWindowClickListener clickListener = new MapboxMap.OnInfoWindowClickListener() {
- @Override
- public boolean onInfoWindowClick(@NonNull Marker marker) {
- return false;
- }
- };
- mapboxMap.setOnInfoWindowClickListener(clickListener);
- assertEquals("InfoWidowClickListener should match", clickListener, mapboxMap.getOnInfoWindowClickListener());
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.OnInfoWindowClickListener clickListener = marker -> false;
+ mapboxMap.setOnInfoWindowClickListener(clickListener);
+ assertEquals(
+ "InfoWidowClickListener should match", clickListener, mapboxMap.getOnInfoWindowClickListener()
+ );
}));
}
@Test
public void testInfoWindowCloseListener() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.OnInfoWindowCloseListener listener = new MapboxMap.OnInfoWindowCloseListener() {
- @Override
- public void onInfoWindowClose(Marker marker) {
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.OnInfoWindowCloseListener listener = marker -> {
- }
- };
- mapboxMap.setOnInfoWindowCloseListener(listener);
- assertEquals("InfoWindowCloseListener should match", listener, mapboxMap.getOnInfoWindowCloseListener());
- }
+ };
+ mapboxMap.setOnInfoWindowCloseListener(listener);
+ assertEquals("InfoWindowCloseListener should match", listener, mapboxMap.getOnInfoWindowCloseListener());
}));
}
@Test
public void testInfoWindowLongClickListener() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MapboxMap.OnInfoWindowLongClickListener listener = new MapboxMap.OnInfoWindowLongClickListener() {
- @Override
- public void onInfoWindowLongClick(Marker marker) {
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MapboxMap.OnInfoWindowLongClickListener listener = marker -> {
- }
- };
- mapboxMap.setOnInfoWindowLongClickListener(listener);
- assertEquals("InfoWindowLongClickListener should match", listener,
- mapboxMap.getOnInfoWindowLongClickListener());
- }
+ };
+ mapboxMap.setOnInfoWindowLongClickListener(listener);
+ assertEquals("InfoWindowLongClickListener should match", listener,
+ mapboxMap.getOnInfoWindowLongClickListener());
}));
}
@@ -322,13 +287,10 @@ public class MapboxMapTest extends BaseActivityTest {
@Test
public void testAddMarker() {
validateTestSetup();
- onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() {
- @Override
- public void onViewAction(UiController uiController, View view) {
- MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
- Marker marker = mapboxMap.addMarker(markerOptions);
- assertTrue("Marker should be contained", mapboxMap.getMarkers().contains(marker));
- }
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ MarkerOptions markerOptions = new MarkerOptions().position(new LatLng());
+ Marker marker = mapboxMap.addMarker(markerOptions);
+ assertTrue("Marker should be contained", mapboxMap.getMarkers().contains(marker));
}));
}
@@ -340,442 +302,369 @@ 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());
+ }));
+ }
+
+ // Tile pre-fetching
+
+ @Test
+ public void testTilePrefetch() {
+ validateTestSetup();
+ onView(withId(R.id.mapView)).perform(new MapboxMapAction((uiController, view) -> {
+ mapboxMap.setPrefetchesTiles(true);
+ assertTrue(mapboxMap.getPrefetchesTiles());
+ mapboxMap.setPrefetchesTiles(false);
+ assertFalse(mapboxMap.getPrefetchesTiles());
}));
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java
new file mode 100644
index 0000000000..7a1fcbf5f3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java
@@ -0,0 +1,41 @@
+package com.mapbox.mapboxsdk.maps;
+
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.camera.CameraAnimationTypeActivity;
+import org.junit.Test;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscape;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscapeReverse;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationPortrait;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationPortraitReverse;
+
+public class OrientationTest extends BaseActivityTest {
+
+ @Test
+ public void testChangeDeviceOrientation() {
+ onView(isRoot()).perform(orientationLandscape());
+ waitLoop(2200);
+ onView(isRoot()).perform(orientationPortrait());
+ waitLoop(2500);
+ onView(isRoot()).perform(orientationLandscapeReverse());
+ waitLoop(500);
+ onView(isRoot()).perform(orientationPortraitReverse());
+ waitLoop(1250);
+ onView(isRoot()).perform(orientationLandscape());
+ waitLoop(750);
+ onView(isRoot()).perform(orientationPortrait());
+ waitLoop(950);
+ onView(isRoot()).perform(orientationLandscapeReverse());
+ onView(isRoot()).perform(orientationPortraitReverse());
+ onView(isRoot()).perform(orientationLandscape());
+ onView(isRoot()).perform(orientationPortrait());
+ }
+
+ @Override
+ protected Class getActivityClass() {
+ return CameraAnimationTypeActivity.class;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/MapboxMapAction.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/MapboxMapAction.java
new file mode 100644
index 0000000000..47af80cab9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/MapboxMapAction.java
@@ -0,0 +1,49 @@
+package com.mapbox.mapboxsdk.testapp.action;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+
+import org.hamcrest.Matcher;
+
+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;
+
+public class MapboxMapAction implements ViewAction {
+
+ private OnInvokeActionListener invokeViewAction;
+ private MapboxMap mapboxMap;
+
+ private MapboxMapAction(OnInvokeActionListener invokeViewAction, MapboxMap mapboxMap) {
+ this.invokeViewAction = invokeViewAction;
+ 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) {
+ invokeViewAction.onInvokeAction(uiController, mapboxMap);
+ }
+
+ public static void invoke(MapboxMap mapboxMap, OnInvokeActionListener invokeViewAction) {
+ onView(withId(android.R.id.content)).perform(new MapboxMapAction(invokeViewAction, mapboxMap));
+ }
+
+ public interface OnInvokeActionListener {
+ void onInvokeAction(UiController uiController, MapboxMap mapboxMap);
+ }
+}
+
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationChangeAction.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationChangeAction.java
new file mode 100644
index 0000000000..7f73d6a7f3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationChangeAction.java
@@ -0,0 +1,74 @@
+package com.mapbox.mapboxsdk.testapp.action;
+
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ActivityInfo;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.view.View;
+import android.view.ViewGroup;
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+
+public class OrientationChangeAction implements ViewAction {
+
+ private final int orientation;
+
+ private OrientationChangeAction(int orientation) {
+ this.orientation = orientation;
+ }
+
+ public static ViewAction orientationLandscape() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ public static ViewAction orientationPortrait() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ public static ViewAction orientationLandscapeReverse() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ }
+
+ public static ViewAction orientationPortraitReverse() {
+ return new OrientationChangeAction(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isRoot();
+ }
+
+ @Override
+ public String getDescription() {
+ return "change orientation to " + orientation;
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadUntilIdle();
+ Activity activity = getActivity(view.getContext());
+ if (activity == null && view instanceof ViewGroup) {
+ ViewGroup v = (ViewGroup) view;
+ int c = v.getChildCount();
+ for (int i = 0; i < c && activity == null; ++i) {
+ activity = getActivity(v.getChildAt(i).getContext());
+ }
+ }
+ activity.setRequestedOrientation(orientation);
+ }
+
+ private Activity getActivity(Context context) {
+ while (context instanceof ContextWrapper) {
+ if (context instanceof Activity) {
+ return (Activity) context;
+ }
+ context = ((ContextWrapper) context).getBaseContext();
+ }
+ return null;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java
index c029bc09c4..3f32443021 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java
@@ -10,18 +10,14 @@ import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.rule.ActivityTestRule;
import android.view.View;
-
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
-
import junit.framework.Assert;
-
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
-
import timber.log.Timber;
import static android.support.test.espresso.Espresso.onView;
@@ -49,8 +45,9 @@ public abstract class BaseActivityTest {
throw new RuntimeException("Could not start test for " + getActivityClass().getSimpleName() + ".\n"
+ "The ViewHierarchy doesn't contain a view with resource id = R.id.mapView or \n"
+ "the Activity doesn't contain an instance variable with a name equal to mapboxMap.\n"
- + "You can resolve this issue be implementing the requirements above or\n add "
- + getActivityClass().getSimpleName() + " to the excludeActivities array in `generate-test-code.js`.\n");
+ + "You can resolve this issue by adding the requirements above or\n add "
+ + getActivityClass().getSimpleName() + " to the platform/android/scripts/exclude-activity-gen.json to blacklist"
+ + " the Activity from being generated.\n");
}
}
@@ -67,12 +64,15 @@ public abstract class BaseActivityTest {
protected abstract Class getActivityClass();
protected void checkViewIsDisplayed(int id) {
- onView(withId(id))
- .check(matches(isDisplayed()));
+ onView(withId(id)).check(matches(isDisplayed()));
}
protected void waitLoop() {
- onView(withId(R.id.mapView)).perform(new LoopAction(500));
+ waitLoop(500);
+ }
+
+ protected void waitLoop(long waitTime) {
+ onView(withId(R.id.mapView)).perform(new LoopAction(waitTime));
}
static boolean isConnected(Context context) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java
new file mode 100644
index 0000000000..33a946d0a1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java
@@ -0,0 +1,146 @@
+package com.mapbox.mapboxsdk.testapp.annotations;
+
+import android.app.Activity;
+import android.support.v4.content.res.ResourcesCompat;
+
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.IconManagerResolver;
+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.IconUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * Tests integration between Icons and Markers
+ */
+public class IconTest extends BaseActivityTest {
+
+ private Map<Icon, Integer> iconMap;
+
+ @Before
+ public void beforeTest() {
+ super.beforeTest();
+ iconMap = new IconManagerResolver(getMapboxMap()).getIconMap();
+ }
+
+ @Test
+ public void testEmpty() throws Exception {
+ assertTrue(iconMap.isEmpty());
+ }
+
+ @Test
+ public void testAddSameIconMarker() throws Exception {
+ Icon defaultMarker = IconFactory.getInstance(rule.getActivity()).defaultMarker();
+ getMapboxMap().addMarker(new MarkerOptions().position(new LatLng()));
+ getMapboxMap().addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ assertEquals(1, iconMap.size());
+ assertEquals(2, iconMap.get(defaultMarker), 0);
+ }
+
+ @Test
+ public void testAddDifferentIconMarker() throws Exception {
+ Icon icon = IconFactory.getInstance(rule.getActivity()).fromResource(R.drawable.mapbox_logo_icon);
+ getMapboxMap().addMarker(new MarkerOptions().icon(icon).position(new LatLng()));
+ getMapboxMap().addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ assertEquals(iconMap.size(), 2);
+ assertTrue(iconMap.containsKey(icon));
+ assertTrue(iconMap.get(icon) == 1);
+ }
+
+ @Test
+ public void testAddRemoveIconMarker() throws Exception {
+ MapboxMap mapboxMap = getMapboxMap();
+
+ Icon icon = IconFactory.getInstance(rule.getActivity()).fromResource(R.drawable.mapbox_logo_icon);
+ Marker marker = mapboxMap.addMarker(new MarkerOptions().icon(icon).position(new LatLng()));
+ mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ assertEquals(iconMap.size(), 2);
+ assertTrue(iconMap.containsKey(icon));
+ assertTrue(iconMap.get(icon) == 1);
+
+ mapboxMap.removeMarker(marker);
+ assertEquals(iconMap.size(), 1);
+ assertFalse(iconMap.containsKey(icon));
+ }
+
+ @Test
+ public void testAddRemoveDefaultMarker() throws Exception {
+ MapboxMap mapboxMap = getMapboxMap();
+
+ Marker marker = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ assertEquals(iconMap.size(), 1);
+
+ mapboxMap.removeMarker(marker);
+ assertEquals(iconMap.size(), 0);
+
+ mapboxMap.addMarker(new MarkerOptions().position(new LatLng()));
+ assertEquals(iconMap.size(), 1);
+ }
+
+ @Test
+ public void testAddRemoveMany() throws Exception {
+ Activity activity = rule.getActivity();
+ MapboxMap mapboxMap = getMapboxMap();
+ IconFactory iconFactory = IconFactory.getInstance(activity);
+
+ // add 2 default icon markers
+ Marker defaultMarkerOne = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1)));
+ Marker defaultMarkerTwo = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(2, 1)));
+
+ // add 4 unique icon markers
+ mapboxMap.addMarker(new MarkerOptions()
+ .icon(iconFactory.fromResource(R.drawable.mapbox_logo_icon))
+ .position(new LatLng(3, 1))
+ );
+ mapboxMap.addMarker(new MarkerOptions()
+ .icon(iconFactory.fromResource(R.drawable.mapbox_compass_icon))
+ .position(new LatLng(4, 1))
+ );
+ mapboxMap.addMarker(new MarkerOptions()
+ .icon(IconUtils.drawableToIcon(activity, R.drawable.ic_stars,
+ ResourcesCompat.getColor(activity.getResources(),
+ R.color.blueAccent, activity.getTheme())))
+ .position(new LatLng(5, 1))
+ );
+ mapboxMap.addMarker(new MarkerOptions()
+ .icon(iconFactory.fromResource(R.drawable.ic_android))
+ .position(new LatLng(6, 1))
+ );
+
+ assertEquals("Amount of icons should match 5", 5, iconMap.size());
+ assertEquals("Refcounter of default marker should match 2", 2, iconMap.get(iconFactory.defaultMarker()), 0);
+
+ mapboxMap.removeMarker(defaultMarkerOne);
+
+ assertEquals("Amount of icons should match 5", 5, iconMap.size());
+ assertEquals("Refcounter of default marker should match 1", 1, iconMap.get(iconFactory.defaultMarker()), 0);
+
+ mapboxMap.removeMarker(defaultMarkerTwo);
+
+ assertEquals("Amount of icons should match 4", 4, iconMap.size());
+ assertNull("DefaultMarker shouldn't exist anymore", iconMap.get(iconFactory.defaultMarker()));
+
+ mapboxMap.clear();
+ assertEquals("Amount of icons should match 0", 0, iconMap.size());
+ }
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+}
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 2f638ff651..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,27 +1,21 @@
package com.mapbox.mapboxsdk.testapp.annotations;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-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.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;
-import org.hamcrest.Matcher;
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 android.support.test.espresso.matcher.ViewMatchers.withText;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class MarkerTest extends BaseActivityTest {
@@ -37,87 +31,39 @@ public class MarkerTest extends BaseActivityTest {
@Ignore
public void addMarkerTest() {
validateTestSetup();
- 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);
+ MarkerOptions options = new MarkerOptions();
+ options.setPosition(new LatLng());
+ options.setSnippet(TestConstants.TEXT_MARKER_SNIPPET);
+ options.setTitle(TestConstants.TEXT_MARKER_TITLE);
+ marker = mapboxMap.addMarker(options);
- onView(withId(R.id.mapView)).perform(new AddMarkerAction(mapboxMap, options));
- assertEquals("Markers sze 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());
+ });
}
@Test
@Ignore
public void showInfoWindowTest() {
validateTestSetup();
-
- final MarkerOptions options = new MarkerOptions();
- options.setPosition(new LatLng());
- options.setSnippet(TestConstants.TEXT_MARKER_SNIPPET);
- options.setTitle(TestConstants.TEXT_MARKER_TITLE);
-
- onView(withId(R.id.mapView)).perform(new AddMarkerAction(mapboxMap, options));
- onView(withId(R.id.mapView)).perform(new ShowInfoWindowAction(mapboxMap));
+ 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()));
}
- private class AddMarkerAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private MarkerOptions options;
-
- AddMarkerAction(MapboxMap map, MarkerOptions markerOptions) {
- mapboxMap = map;
- options = markerOptions;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- marker = mapboxMap.addMarker(options);
- }
- }
-
- private class ShowInfoWindowAction implements ViewAction {
-
- private MapboxMap mapboxMap;
-
- ShowInfoWindowAction(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.selectMarker(marker);
-
- }
- }
}
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 bee20c3150..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,28 +1,21 @@
package com.mapbox.mapboxsdk.testapp.annotations;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.view.View;
-
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.geometry.LatLng;
-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.annotation.MarkerViewActivity;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerViewOptions;
import com.mapbox.mapboxsdk.testapp.utils.TestConstants;
-import org.hamcrest.Matcher;
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 android.support.test.espresso.matcher.ViewMatchers.withText;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class MarkerViewTest extends BaseActivityTest {
@@ -38,20 +31,23 @@ public class MarkerViewTest extends BaseActivityTest {
@Ignore
public void addMarkerViewTest() {
validateTestSetup();
- 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);
-
- onView(withId(R.id.mapView)).perform(new AddTextMarkerViewAction(mapboxMap, options));
- assertEquals("Markers sze 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());
+ addAdapter();
+ 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);
+ });
onView(withText(TestConstants.TEXT_MARKER_TEXT)).check(matches(isDisplayed()));
}
@@ -59,71 +55,25 @@ public class MarkerViewTest extends BaseActivityTest {
@Ignore
public void showInfoWindowTest() {
validateTestSetup();
-
- 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);
-
- onView(withId(R.id.mapView)).perform(new AddTextMarkerViewAction(mapboxMap, options));
+ addAdapter();
+ 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(withId(R.id.mapView)).perform(new ShowInfoWindowAction(mapboxMap));
onView(withText(TestConstants.TEXT_MARKER_TITLE)).check(matches(isDisplayed()));
onView(withText(TestConstants.TEXT_MARKER_SNIPPET)).check(matches(isDisplayed()));
}
- private class AddTextMarkerViewAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private TextMarkerViewOptions options;
-
- AddTextMarkerViewAction(MapboxMap map, TextMarkerViewOptions markerOptions) {
- mapboxMap = map;
- options = markerOptions;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- mapboxMap.getMarkerViewManager().addMarkerViewAdapter(
- new MarkerViewActivity.TextAdapter(view.getContext(), mapboxMap));
- marker = mapboxMap.addMarker(options);
- uiController.loopMainThreadForAtLeast(250);
- }
+ private void addAdapter() {
+ invoke(mapboxMap, (uiController, mapboxMap) -> mapboxMap.getMarkerViewManager().addMarkerViewAdapter(
+ new MarkerViewActivity.TextAdapter(rule.getActivity(), mapboxMap)));
}
- private class ShowInfoWindowAction implements ViewAction {
-
- private MapboxMap mapboxMap;
-
- ShowInfoWindowAction(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.selectMarker(marker);
- uiController.loopMainThreadForAtLeast(250);
- }
- }
}
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 325568bec4..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,25 +1,17 @@
package com.mapbox.mapboxsdk.testapp.annotations;
import android.graphics.Color;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.view.View;
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.R;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.hamcrest.Matcher;
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 com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class PolygonTest extends BaseActivityTest {
@@ -29,60 +21,32 @@ public class PolygonTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private Polygon polygon;
-
@Test
@Ignore
- /** native crash **/
public void addPolygonTest() {
validateTestSetup();
- 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);
-
- onView(withId(R.id.mapView)).perform(new AddPolygonAction(mapboxMap, 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());
- }
-
- private class AddPolygonAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private PolygonOptions options;
-
- AddPolygonAction(MapboxMap map, PolygonOptions polygonOptions) {
- mapboxMap = map;
- options = polygonOptions;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- polygon = mapboxMap.addPolygon(options);
- }
+ 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 91553f7042..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,83 +1,48 @@
package com.mapbox.mapboxsdk.testapp.annotations;
import android.graphics.Color;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.view.View;
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.R;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.hamcrest.Matcher;
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 com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class PolylineTest extends BaseActivityTest {
- private Polyline polyline;
-
@Override
protected Class getActivityClass() {
return EspressoTestActivity.class;
}
- @Ignore
@Test
+ @Ignore
public void addPolylineTest() {
validateTestSetup();
- 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);
-
- onView(withId(R.id.mapView)).perform(new AddPolyLineAction(mapboxMap, 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());
- }
-
- private class AddPolyLineAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private PolylineOptions options;
-
- AddPolyLineAction(MapboxMap map, PolylineOptions polylineOptions) {
- mapboxMap = map;
- options = polylineOptions;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- polyline = mapboxMap.addPolyline(options);
- }
+ 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 af1a146ca2..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,28 +1,19 @@
package com.mapbox.mapboxsdk.testapp.camera;
import android.graphics.PointF;
-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.camera.CameraUpdate;
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.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.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 com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class CameraAnimateTest extends BaseActivityTest {
@@ -36,202 +27,172 @@ public class CameraAnimateTest extends BaseActivityTest {
@Ignore
public void testAnimateToCameraPositionTarget() {
validateTestSetup();
-
- /*TODO remove zoom #6474*/
- 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);
-
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap, CameraUpdateFactory.newLatLng(moveTarget)));
- 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);
+ });
}
@Test
@Ignore
public void testAnimateToCameraPositionTargetZoom() {
validateTestSetup();
-
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
-
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom)));
- 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);
+ });
}
@Test
@Ignore
public void testAnimateToCameraPosition() {
validateTestSetup();
+ 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;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- onView(withId(R.id.mapView)).perform(
- new AnimateCameraAction(mapboxMap, CameraUpdateFactory.newCameraPosition(
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(
new CameraPosition.Builder()
.target(moveTarget)
.zoom(moveZoom)
.tilt(moveTilt)
.bearing(moveBearing)
- .build()))
- );
-
- 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);
+ .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);
+ });
}
@Test
@Ignore
public void testAnimateToBounds() {
validateTestSetup();
-
- 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);
-
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngBounds(builder.build(), 0)));
-
- 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);
+ });
}
@Test
@Ignore
public void testAnimateToMoveBy() {
validateTestSetup();
-
- final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
- final LatLng moveTarget = new LatLng(2, 2);
- final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
-
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap, CameraUpdateFactory.scrollBy(
- moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y)));
-
- 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);
+ });
}
@Test
@Ignore
public void testAnimateToZoomIn() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 1.0f;
-
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap, CameraUpdateFactory.zoomIn()));
- 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);
+ });
}
@Test
@Ignore
public void testAnimateToZoomOut() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 10.0f;
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom)));
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap, CameraUpdateFactory.zoomOut()));
- 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);
+ });
}
@Test
@Ignore
public void testAnimateToZoomBy() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
-
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap, CameraUpdateFactory.zoomBy(zoomBy)));
- 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);
+ });
}
@Test
@Ignore
public void testAnimateToZoomTo() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- final float zoomTo = 2.45f;
-
- onView(withId(R.id.mapView)).perform(new AnimateCameraAction(mapboxMap, CameraUpdateFactory.zoomTo(zoomTo)));
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
- TestConstants.ZOOM_DELTA);
- }
-
- private class AnimateCameraAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private CameraUpdate cameraUpdate;
-
- AnimateCameraAction(MapboxMap map, CameraUpdate update) {
- mapboxMap = map;
- cameraUpdate = update;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- mapboxMap.animateCamera(cameraUpdate);
+ 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 4fae894039..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,28 +1,19 @@
package com.mapbox.mapboxsdk.testapp.camera;
import android.graphics.PointF;
-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.camera.CameraUpdate;
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.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.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 com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class CameraEaseTest extends BaseActivityTest {
@@ -36,201 +27,171 @@ public class CameraEaseTest extends BaseActivityTest {
@Ignore
public void testEaseToCameraPositionTarget() {
validateTestSetup();
-
- /*TODO remove zoom #6474*/
- 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);
-
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap, CameraUpdateFactory.newLatLng(moveTarget)));
- 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);
+ });
}
@Test
@Ignore
public void testEaseToCameraPositionTargetZoom() {
validateTestSetup();
-
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
-
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom)));
- 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);
+ });
}
@Test
@Ignore
public void testEaseToCameraPosition() {
validateTestSetup();
+ 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;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- onView(withId(R.id.mapView)).perform(
- new EaseCameraAction(mapboxMap, CameraUpdateFactory.newCameraPosition(
+ mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(
new CameraPosition.Builder()
.target(moveTarget)
.zoom(moveZoom)
.tilt(moveTilt)
.bearing(moveBearing)
- .build()))
- );
-
- 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);
+ .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);
+ });
}
@Test
@Ignore
public void testEaseToBounds() {
validateTestSetup();
-
- 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);
-
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngBounds(builder.build(), 0)));
-
- 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);
+ });
}
@Test
@Ignore
public void testEaseToMoveBy() {
validateTestSetup();
-
- final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
- final LatLng moveTarget = new LatLng(2, 2);
- final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
-
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap, CameraUpdateFactory.scrollBy(
- moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y)));
-
- 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);
+ });
}
@Test
@Ignore
public void testEaseToZoomIn() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 1.0f;
-
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap, CameraUpdateFactory.zoomIn()));
- 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);
+ });
}
@Test
@Ignore
public void testEaseToZoomOut() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 10.0f;
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom)));
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap, CameraUpdateFactory.zoomOut()));
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", 9, cameraPosition.zoom, 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);
+ });
}
@Test
@Ignore
public void testEaseToZoomBy() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
-
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap, CameraUpdateFactory.zoomBy(zoomBy)));
- 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);
+ });
}
@Test
@Ignore
public void testEaseToZoomTo() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- final float zoomTo = 2.45f;
-
- onView(withId(R.id.mapView)).perform(new EaseCameraAction(mapboxMap, CameraUpdateFactory.zoomTo(zoomTo)));
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
- TestConstants.ZOOM_DELTA);
- }
-
- private class EaseCameraAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private CameraUpdate cameraUpdate;
-
- EaseCameraAction(MapboxMap map, CameraUpdate update) {
- mapboxMap = map;
- cameraUpdate = update;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- mapboxMap.easeCamera(cameraUpdate);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ final float zoomTo = 2.45f;
+ mapboxMap.easeCamera(CameraUpdateFactory.zoomTo(zoomTo));
uiController.loopMainThreadForAtLeast(TestConstants.ANIMATION_TEST_TIME);
- }
+ CameraPosition cameraPosition = mapboxMap.getCameraPosition();
+ assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
+ TestConstants.ZOOM_DELTA);
+ });
}
-}
-
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java
index ea8398fc8e..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,28 +1,20 @@
+
package com.mapbox.mapboxsdk.testapp.camera;
import android.graphics.PointF;
-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.camera.CameraUpdate;
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.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.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 com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.assertEquals;
public class CameraMoveTest extends BaseActivityTest {
@@ -36,201 +28,172 @@ public class CameraMoveTest extends BaseActivityTest {
@Ignore
public void testMoveToCameraPositionTarget() {
validateTestSetup();
-
- /*TODO remove zoom #6474*/
- 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);
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap, CameraUpdateFactory.newLatLng(moveTarget)));
- 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);
+ });
}
@Test
@Ignore
public void testMoveToCameraPositionTargetZoom() {
validateTestSetup();
-
- final float moveZoom = 15.5f;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngZoom(moveTarget, moveZoom)));
- 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);
+ });
}
@Test
@Ignore
public void testMoveToCameraPosition() {
validateTestSetup();
+ 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;
- final LatLng moveTarget = new LatLng(1.0000000001, 1.0000000003);
- final float moveZoom = 15.5f;
- final float moveTilt = 45.5f;
- final float moveBearing = 12.5f;
-
- onView(withId(R.id.mapView)).perform(
- new MoveCameraAction(mapboxMap, CameraUpdateFactory.newCameraPosition(
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
new CameraPosition.Builder()
.target(moveTarget)
.zoom(moveZoom)
.tilt(moveTilt)
.bearing(moveBearing)
- .build()))
- );
-
- 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);
+ .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);
+ });
}
@Test
@Ignore
public void testMoveToBounds() {
validateTestSetup();
-
- 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);
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngBounds(builder.build(), 0)));
-
- 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);
+ });
}
@Test
@Ignore
public void testMoveToMoveBy() {
validateTestSetup();
-
- final PointF centerPoint = mapboxMap.getProjection().toScreenLocation(mapboxMap.getCameraPosition().target);
- final LatLng moveTarget = new LatLng(2, 2);
- final PointF moveTargetPoint = mapboxMap.getProjection().toScreenLocation(moveTarget);
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap, CameraUpdateFactory.scrollBy(
- moveTargetPoint.x - centerPoint.x, moveTargetPoint.y - centerPoint.y)));
-
- 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);
+ });
}
@Test
@Ignore
public void testMoveToZoomIn() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 1.0f;
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap, CameraUpdateFactory.zoomIn()));
- 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);
+ });
}
@Test
@Ignore
public void testMoveToZoomOut() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 10.0f;
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap,
- CameraUpdateFactory.newLatLngZoom(new LatLng(), zoom)));
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap, CameraUpdateFactory.zoomOut()));
- 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);
+ });
}
@Test
@Ignore
public void testMoveToZoomBy() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- float zoom = 1.0f;
- final float zoomBy = 2.45f;
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap, CameraUpdateFactory.zoomBy(zoomBy)));
- 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);
+ });
}
@Test
@Ignore
public void testMoveToZoomTo() {
validateTestSetup();
-
- /*TODO fix zoom #6474*/
- final float zoomTo = 2.45f;
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap, CameraUpdateFactory.zoomTo(zoomTo)));
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Moved camera zoom should match moved camera zoom", cameraPosition.zoom, zoomTo,
- TestConstants.ZOOM_DELTA);
- }
-
- private class MoveCameraAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private CameraUpdate cameraUpdate;
-
- MoveCameraAction(MapboxMap map, CameraUpdate update) {
- mapboxMap = map;
- cameraUpdate = update;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- mapboxMap.moveCamera(cameraUpdate);
- uiController.loopMainThreadForAtLeast(100);
- }
+ 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
new file mode 100644
index 0000000000..738f1e203f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java
@@ -0,0 +1,34 @@
+package com.mapbox.mapboxsdk.testapp.geometry;
+
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.feature.QueryRenderedFeaturesBoxHighlightActivity;
+
+import org.junit.Test;
+
+/**
+ * Instrumentation test to validate integration of LatLngBounds
+ */
+public class LatLngBoundsTest extends BaseActivityTest {
+
+ @Override
+ protected Class getActivityClass() {
+ return QueryRenderedFeaturesBoxHighlightActivity.class;
+ }
+
+ @Test
+ public void testLatLngBounds() {
+ // regression test for #9322
+ validateTestSetup();
+ 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 d37c6db2d5..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
@@ -21,6 +21,7 @@ import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import static android.support.test.espresso.Espresso.onData;
@@ -67,6 +68,7 @@ public class AttributionTest extends BaseActivityTest {
}
@Test
+ @Ignore
public void testMapboxStreetsMapboxAttributionLink() {
validateTestSetup();
if (urlSpans == null) {
@@ -87,6 +89,7 @@ public class AttributionTest extends BaseActivityTest {
}
@Test
+ @Ignore
public void testMapboxStreetsOpenStreetMapAttributionLink() {
validateTestSetup();
if (urlSpans == null) {
@@ -107,6 +110,7 @@ public class AttributionTest extends BaseActivityTest {
}
@Test
+ @Ignore
public void testImproveMapLink() {
validateTestSetup();
if (urlSpans == null) {
@@ -146,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 f15605042b..2a510b4dc5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java
@@ -1,20 +1,13 @@
package com.mapbox.mapboxsdk.testapp.maps.widgets;
-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.camera.CameraUpdate;
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.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
import com.mapbox.mapboxsdk.testapp.utils.TestConstants;
-import org.hamcrest.Matcher;
import org.junit.Ignore;
import org.junit.Test;
@@ -23,6 +16,7 @@ import static android.support.test.espresso.action.ViewActions.click;
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 com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
@@ -40,96 +34,40 @@ public class CompassViewTest extends BaseActivityTest {
}
@Test
+ @Ignore
public void testVisible() {
validateTestSetup();
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap,
- CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .bearing(45)
- .zoom(1)
- .target(new LatLng())
- .build()
- )
- )
- );
- waitLoop();
+ 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()));
}
@Test
- @Ignore // 10-31-2016 click action is not working
+ @Ignore
public void testClick() {
validateTestSetup();
-
- onView(withId(R.id.mapView)).perform(new MoveCameraAction(mapboxMap,
- CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .bearing(45)
- .zoom(1)
- .target(new LatLng())
- .build()
- )
- )
- );
-
- waitLoop();
+ 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());
- onView(withId(R.id.mapView)).perform(new WaitAction(3000));
+ waitLoop();
onView(withId(R.id.compassView)).check(matches(not(isDisplayed())));
-
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Camera bearing should face north, ", 0, cameraPosition.bearing, TestConstants.BEARING_DELTA);
- }
-
- private class WaitAction implements ViewAction {
-
- private long waitTime;
-
- WaitAction(long waitTime) {
- this.waitTime = waitTime;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- uiController.loopMainThreadForAtLeast(waitTime);
- }
- }
-
- private class MoveCameraAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private CameraUpdate cameraUpdate;
-
- MoveCameraAction(MapboxMap map, CameraUpdate update) {
- mapboxMap = map;
- cameraUpdate = update;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- mapboxMap.moveCamera(cameraUpdate);
- }
+ 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
index efd67db356..cf58ba50a6 100644
--- 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
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.testapp.maps.widgets;
+import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -9,12 +10,12 @@ 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.location.LocationSource;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.mapboxsdk.testapp.R;
@@ -97,6 +98,7 @@ public class MyLocationViewTest extends BaseActivityTest {
return getClass().getSimpleName();
}
+ @SuppressLint("MissingPermission")
@Override
public void perform(UiController uiController, View view) {
if (isEnabled) {
@@ -104,7 +106,7 @@ public class MyLocationViewTest extends BaseActivityTest {
mapboxMap.moveCamera(
CameraUpdateFactory.newCameraPosition(
new CameraPosition.Builder()
- .target(new LatLng(LocationSource.getLocationEngine(view.getContext()).getLastLocation()))
+ .target(new LatLng(Mapbox.getLocationEngine().getLastLocation()))
.build()
)
);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java
new file mode 100644
index 0000000000..84f84fdb90
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java
@@ -0,0 +1,44 @@
+package com.mapbox.mapboxsdk.testapp.offline;
+
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.OfflineUtils;
+
+import org.junit.Test;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+import static com.mapbox.mapboxsdk.testapp.activity.offline.OfflineActivity.JSON_CHARSET;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+public class OfflineUtilsTest extends BaseActivityTest {
+
+ private static final String REGION_NAME = "hello world";
+ private static final String CONVERTED_REGION_NAME = "{\"FIELD_REGION_NAME\":\"hello world\"}";
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+
+ @Test
+ public void testOfflineUtilsConvertToBytes() throws UnsupportedEncodingException {
+ byte[] expected = CONVERTED_REGION_NAME.getBytes(JSON_CHARSET);
+ byte[] actual = OfflineUtils.convertRegionName(REGION_NAME);
+ assertTrue("Bytes arrays should match", Arrays.equals(expected, actual));
+ }
+
+ @Test
+ public void testOfflineUtilsConvertToString() throws UnsupportedEncodingException {
+ String actual = OfflineUtils.convertRegionName(CONVERTED_REGION_NAME.getBytes(JSON_CHARSET));
+ assertEquals("Strings should match", REGION_NAME, actual);
+ }
+
+ @Test
+ public void testOfflineUtilsConvertNoOp() {
+ String convertNoOp = OfflineUtils.convertRegionName(OfflineUtils.convertRegionName(REGION_NAME));
+ assertEquals("Strings should match", REGION_NAME, convertNoOp);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java
index 851660f06e..50f2fa7d62 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,42 +3,34 @@
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
-import android.support.test.espresso.Espresso;
-import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
-import timber.log.Timber;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.style.functions.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.CameraFunction;
-import com.mapbox.mapboxsdk.style.functions.SourceFunction;
-import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
-import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops;
-import com.mapbox.mapboxsdk.style.functions.stops.Stop;
-import com.mapbox.mapboxsdk.style.functions.stops.Stops;
import com.mapbox.mapboxsdk.style.layers.BackgroundLayer;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
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 org.junit.Assert.*;
-import static com.mapbox.mapboxsdk.style.layers.Property.*;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+import timber.log.Timber;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+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.functions.stops.Stops.interval;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundPattern;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* Basic smoke tests for BackgroundLayer
@@ -53,9 +45,9 @@ public class BackgroundLayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer(){
+ private void setupLayer() {
Timber.i("Retrieving layer");
- layer = mapboxMap.getLayerAs("background");
+ invoke(mapboxMap, (uiController, mapboxMap) -> layer = mapboxMap.getLayerAs("background"));
}
@Test
@@ -63,14 +55,16 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- 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);
+ });
}
@Test
@@ -78,12 +72,14 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-colorTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setBackgroundColorTransition(options);
- assertEquals(layer.getBackgroundColorTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setBackgroundColorTransition(options);
+ assertEquals(layer.getBackgroundColorTransition(), options);
+ });
}
@Test
@@ -91,11 +87,13 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-color");
- 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)");
+ // Set and Get
+ layer.setProperties(backgroundColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getBackgroundColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -103,26 +101,28 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- backgroundColor(
- zoom(
- exponential(
- stop(2, backgroundColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -130,11 +130,13 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-color");
- 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);
+ });
}
@Test
@@ -142,12 +144,14 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-patternTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setBackgroundPatternTransition(options);
- assertEquals(layer.getBackgroundPatternTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setBackgroundPatternTransition(options);
+ assertEquals(layer.getBackgroundPatternTransition(), options);
+ });
}
@Test
@@ -155,11 +159,13 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-pattern");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(backgroundPattern("pedestrian-polygon"));
- assertEquals((String) layer.getBackgroundPattern().getValue(), (String) "pedestrian-polygon");
+ // Set and Get
+ layer.setProperties(backgroundPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getBackgroundPattern().getValue(), (String) "pedestrian-polygon");
+ });
}
@Test
@@ -167,25 +173,27 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-pattern");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- backgroundPattern(
- zoom(
- interval(
- stop(2, backgroundPattern("pedestrian-polygon"))
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ backgroundPattern(
+ zoom(
+ interval(
+ stop(2, backgroundPattern("pedestrian-polygon"))
+ )
)
)
- )
- );
-
- // 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -193,12 +201,14 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-opacityTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setBackgroundOpacityTransition(options);
- assertEquals(layer.getBackgroundOpacityTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setBackgroundOpacityTransition(options);
+ assertEquals(layer.getBackgroundOpacityTransition(), options);
+ });
}
@Test
@@ -206,11 +216,13 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(backgroundOpacity(0.3f));
- assertEquals((Float) layer.getBackgroundOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(backgroundOpacity(0.3f));
+ assertEquals((Float) layer.getBackgroundOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -218,26 +230,28 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- backgroundOpacity(
- zoom(
- exponential(
- stop(2, backgroundOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ backgroundOpacity(
+ zoom(
+ exponential(
+ stop(2, backgroundOpacity(0.3f))
+ ).withBase(0.5f)
+ )
)
- )
- );
-
- // 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());
+ );
+
+ // 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());
+ });
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BaseStyleTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BaseStyleTest.java
deleted file mode 100644
index 3d56752150..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BaseStyleTest.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.style;
-
-import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-
-/**
- * Base Test class for Style tests
- */
-public class BaseStyleTest extends BaseActivityTest {
-
- @Override
- protected Class getActivityClass() {
- return RuntimeStyleTestActivity.class;
- }
-}
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 bf31a935f8..af6ed7f5b9 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,14 +3,10 @@
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
-import android.support.test.espresso.Espresso;
-import android.support.test.rule.ActivityTestRule;
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.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.SourceFunction;
import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
@@ -19,26 +15,42 @@ 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.CircleLayer;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
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 org.junit.Assert.*;
-import static com.mapbox.mapboxsdk.style.layers.Property.*;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+import timber.log.Timber;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+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.layers.Property.CIRCLE_PITCH_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.CIRCLE_PITCH_SCALE_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.CIRCLE_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleBlur;
+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.circlePitchAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circlePitchScale;
+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.circleStrokeOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* Basic smoke tests for CircleLayer
@@ -53,15 +65,18 @@ public class CircleLayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer(){
- 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");
- }
+ private void setupLayer() {
+ Timber.i("Retrieving 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");
+ }
+ });
}
@Test
@@ -69,14 +84,16 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- 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);
+ });
}
@Test
@@ -84,15 +101,17 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("SourceLayer");
- 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);
+ });
}
@Test
@@ -100,12 +119,14 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-radiusTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setCircleRadiusTransition(options);
- assertEquals(layer.getCircleRadiusTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleRadiusTransition(options);
+ assertEquals(layer.getCircleRadiusTransition(), options);
+ });
}
@Test
@@ -113,11 +134,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-radius");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleRadius(0.3f));
- assertEquals((Float) layer.getCircleRadius().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(circleRadius(0.3f));
+ assertEquals((Float) layer.getCircleRadius().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -125,26 +148,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-radius");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleRadius(
- zoom(
- exponential(
- stop(2, circleRadius(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -152,19 +177,21 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-radius");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
}
@Test
@@ -172,26 +199,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-radius");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleRadius(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleRadius(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -199,29 +228,32 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-radius");
- 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
+ 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());
+ });
+
}
@Test
@@ -229,34 +261,36 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-radius");
- 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);
+ invoke(mapboxMap, (uiController, 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);
+ });
}
@Test
@@ -264,12 +298,14 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-colorTransitionOptions");
- 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);
+ });
}
@Test
@@ -277,11 +313,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(circleColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getCircleColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -289,26 +327,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleColor(
- zoom(
- exponential(
- stop(2, circleColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -316,19 +356,21 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-color");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
}
@Test
@@ -336,26 +378,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, circleColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -363,29 +407,32 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-color");
- 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());
+ invoke(mapboxMap, (uiController, 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
@@ -393,11 +440,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleColor(Color.RED));
- assertEquals(layer.getCircleColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(circleColor(Color.RED));
+ assertEquals(layer.getCircleColorAsInt(), Color.RED);
+ });
}
@Test
@@ -405,12 +454,14 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blurTransitionOptions");
- 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);
+ });
}
@Test
@@ -418,11 +469,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleBlur(0.3f));
- assertEquals((Float) layer.getCircleBlur().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(circleBlur(0.3f));
+ assertEquals((Float) layer.getCircleBlur().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -430,26 +483,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleBlur(
- zoom(
- exponential(
- stop(2, circleBlur(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -457,19 +512,21 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blur");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
}
@Test
@@ -477,26 +534,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleBlur(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleBlur(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -504,29 +563,32 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blur");
- 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());
+ invoke(mapboxMap, (uiController, 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
@@ -534,34 +596,36 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blur");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -569,12 +633,14 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-opacityTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setCircleOpacityTransition(options);
- assertEquals(layer.getCircleOpacityTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleOpacityTransition(options);
+ assertEquals(layer.getCircleOpacityTransition(), options);
+ });
}
@Test
@@ -582,11 +648,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleOpacity(0.3f));
- assertEquals((Float) layer.getCircleOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(circleOpacity(0.3f));
+ assertEquals((Float) layer.getCircleOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -594,26 +662,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleOpacity(
- zoom(
- exponential(
- stop(2, circleOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -621,19 +691,21 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-opacity");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
}
@Test
@@ -641,26 +713,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -668,29 +742,32 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-opacity");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
+
}
@Test
@@ -698,34 +775,36 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-opacity");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -733,12 +812,14 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-translateTransitionOptions");
- 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);
+ });
}
@Test
@@ -746,11 +827,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-translate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleTranslate(new Float[]{0f,0f}));
- assertEquals((Float[]) layer.getCircleTranslate().getValue(), (Float[]) new Float[]{0f,0f});
+ // Set and Get
+ layer.setProperties(circleTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getCircleTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
+ });
}
@Test
@@ -758,26 +841,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-translate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleTranslate(
- zoom(
- exponential(
- stop(2, circleTranslate(new Float[]{0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -785,11 +870,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-translate-anchor");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleTranslateAnchor(CIRCLE_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getCircleTranslateAnchor().getValue(), (String) CIRCLE_TRANSLATE_ANCHOR_MAP);
+ // Set and Get
+ layer.setProperties(circleTranslateAnchor(CIRCLE_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getCircleTranslateAnchor().getValue(), (String) CIRCLE_TRANSLATE_ANCHOR_MAP);
+ });
}
@Test
@@ -797,25 +884,27 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-translate-anchor");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleTranslateAnchor(
- zoom(
- interval(
- stop(2, circleTranslateAnchor(CIRCLE_TRANSLATE_ANCHOR_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -823,11 +912,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-pitch-scale");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circlePitchScale(CIRCLE_PITCH_SCALE_MAP));
- assertEquals((String) layer.getCirclePitchScale().getValue(), (String) CIRCLE_PITCH_SCALE_MAP);
+ // Set and Get
+ layer.setProperties(circlePitchScale(CIRCLE_PITCH_SCALE_MAP));
+ assertEquals((String) layer.getCirclePitchScale().getValue(), (String) CIRCLE_PITCH_SCALE_MAP);
+ });
}
@Test
@@ -835,25 +926,69 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-pitch-scale");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circlePitchScale(
- zoom(
- interval(
- stop(2, circlePitchScale(CIRCLE_PITCH_SCALE_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testCirclePitchAlignmentAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("circle-pitch-alignment");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getCirclePitchAlignment().getValue(), (String) CIRCLE_PITCH_ALIGNMENT_MAP);
+ });
+ }
+
+ @Test
+ public void testCirclePitchAlignmentAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("circle-pitch-alignment");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ circlePitchAlignment(
+ zoom(
+ interval(
+ stop(2, circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -861,12 +996,14 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-widthTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setCircleStrokeWidthTransition(options);
- assertEquals(layer.getCircleStrokeWidthTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleStrokeWidthTransition(options);
+ assertEquals(layer.getCircleStrokeWidthTransition(), options);
+ });
}
@Test
@@ -874,11 +1011,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleStrokeWidth(0.3f));
- assertEquals((Float) layer.getCircleStrokeWidth().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(circleStrokeWidth(0.3f));
+ assertEquals((Float) layer.getCircleStrokeWidth().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -886,26 +1025,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeWidth(
- zoom(
- exponential(
- stop(2, circleStrokeWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -913,19 +1054,21 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-width");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
}
@Test
@@ -933,26 +1076,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleStrokeWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -960,29 +1105,32 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-width");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
+
}
@Test
@@ -990,34 +1138,36 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-width");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -1025,12 +1175,14 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-colorTransitionOptions");
- 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);
+ });
}
@Test
@@ -1038,11 +1190,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(circleStrokeColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getCircleStrokeColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -1050,26 +1204,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeColor(
- zoom(
- exponential(
- stop(2, circleStrokeColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -1077,19 +1233,21 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-color");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
}
@Test
@@ -1097,26 +1255,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, circleStrokeColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -1124,29 +1284,32 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-color");
- 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());
+ invoke(mapboxMap, (uiController, 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
@@ -1154,11 +1317,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleStrokeColor(Color.RED));
- assertEquals(layer.getCircleStrokeColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(circleStrokeColor(Color.RED));
+ assertEquals(layer.getCircleStrokeColorAsInt(), Color.RED);
+ });
}
@Test
@@ -1166,12 +1331,14 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-opacityTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setCircleStrokeOpacityTransition(options);
- assertEquals(layer.getCircleStrokeOpacityTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleStrokeOpacityTransition(options);
+ assertEquals(layer.getCircleStrokeOpacityTransition(), options);
+ });
}
@Test
@@ -1179,11 +1346,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(circleStrokeOpacity(0.3f));
- assertEquals((Float) layer.getCircleStrokeOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(circleStrokeOpacity(0.3f));
+ assertEquals((Float) layer.getCircleStrokeOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1191,26 +1360,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeOpacity(
- zoom(
- exponential(
- stop(2, circleStrokeOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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
@@ -1218,19 +1389,21 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-opacity");
- 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);
+
+ // 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());
+ });
}
@Test
@@ -1238,26 +1411,28 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleStrokeOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -1265,29 +1440,32 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-opacity");
- 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());
+ invoke(mapboxMap, (uiController, 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());
+ });
+
}
@Test
@@ -1295,34 +1473,36 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-opacity");
- 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);
+ invoke(mapboxMap, (uiController, 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);
+ });
}
}
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 fec9a6c119..3a1cba5cf2 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,14 +3,10 @@
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
-import android.support.test.espresso.Espresso;
-import android.support.test.rule.ActivityTestRule;
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.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.SourceFunction;
import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
@@ -19,26 +15,36 @@ 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.FillExtrusionLayer;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
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 org.junit.Assert.*;
-import static com.mapbox.mapboxsdk.style.layers.Property.*;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+import timber.log.Timber;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+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.layers.Property.FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+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;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionPattern;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* Basic smoke tests for FillExtrusionLayer
@@ -53,15 +59,18 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer(){
- 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");
- }
+ private void setupLayer() {
+ Timber.i("Retrieving 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");
+ }
+ });
}
@Test
@@ -69,14 +78,16 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- 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);
+ });
}
@Test
@@ -84,15 +95,17 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("SourceLayer");
- 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);
+ });
}
@Test
@@ -100,12 +113,14 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-opacityTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillExtrusionOpacityTransition(options);
- assertEquals(layer.getFillExtrusionOpacityTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionOpacityTransition(options);
+ assertEquals(layer.getFillExtrusionOpacityTransition(), options);
+ });
}
@Test
@@ -113,11 +128,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillExtrusionOpacity(0.3f));
- assertEquals((Float) layer.getFillExtrusionOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(fillExtrusionOpacity(0.3f));
+ assertEquals((Float) layer.getFillExtrusionOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -125,26 +142,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionOpacity(
- zoom(
- exponential(
- stop(2, fillExtrusionOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -152,12 +171,14 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-colorTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillExtrusionColorTransition(options);
- assertEquals(layer.getFillExtrusionColorTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionColorTransition(options);
+ assertEquals(layer.getFillExtrusionColorTransition(), options);
+ });
}
@Test
@@ -165,11 +186,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(fillExtrusionColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillExtrusionColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -177,26 +200,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionColor(
- zoom(
- exponential(
- stop(2, fillExtrusionColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -204,19 +229,21 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- fillExtrusionColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -224,26 +251,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, fillExtrusionColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -251,29 +280,32 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", fillExtrusionColor(Color.RED))
- )
- ).withDefaultValue(fillExtrusionColor(Color.GREEN))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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
@@ -281,11 +313,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillExtrusionColor(Color.RED));
- assertEquals(layer.getFillExtrusionColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(fillExtrusionColor(Color.RED));
+ assertEquals(layer.getFillExtrusionColorAsInt(), Color.RED);
+ });
}
@Test
@@ -293,12 +327,14 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-translateTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillExtrusionTranslateTransition(options);
- assertEquals(layer.getFillExtrusionTranslateTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionTranslateTransition(options);
+ assertEquals(layer.getFillExtrusionTranslateTransition(), options);
+ });
}
@Test
@@ -306,11 +342,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-translate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillExtrusionTranslate(new Float[]{0f,0f}));
- assertEquals((Float[]) layer.getFillExtrusionTranslate().getValue(), (Float[]) new Float[]{0f,0f});
+ // Set and Get
+ layer.setProperties(fillExtrusionTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getFillExtrusionTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
+ });
}
@Test
@@ -318,26 +356,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-translate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionTranslate(
- zoom(
- exponential(
- stop(2, fillExtrusionTranslate(new Float[]{0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -345,11 +385,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-translate-anchor");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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);
+ // Set and Get
+ layer.setProperties(fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getFillExtrusionTranslateAnchor().getValue(), (String) FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP);
+ });
}
@Test
@@ -357,25 +399,27 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-translate-anchor");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionTranslateAnchor(
- zoom(
- interval(
- stop(2, fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -383,12 +427,14 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-patternTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillExtrusionPatternTransition(options);
- assertEquals(layer.getFillExtrusionPatternTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionPatternTransition(options);
+ assertEquals(layer.getFillExtrusionPatternTransition(), options);
+ });
}
@Test
@@ -396,11 +442,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-pattern");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillExtrusionPattern("pedestrian-polygon"));
- assertEquals((String) layer.getFillExtrusionPattern().getValue(), (String) "pedestrian-polygon");
+ // Set and Get
+ layer.setProperties(fillExtrusionPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getFillExtrusionPattern().getValue(), (String) "pedestrian-polygon");
+ });
}
@Test
@@ -408,25 +456,27 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-pattern");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionPattern(
- zoom(
- interval(
- stop(2, fillExtrusionPattern("pedestrian-polygon"))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -434,12 +484,14 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-heightTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillExtrusionHeightTransition(options);
- assertEquals(layer.getFillExtrusionHeightTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionHeightTransition(options);
+ assertEquals(layer.getFillExtrusionHeightTransition(), options);
+ });
}
@Test
@@ -447,11 +499,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-height");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillExtrusionHeight(0.3f));
- assertEquals((Float) layer.getFillExtrusionHeight().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(fillExtrusionHeight(0.3f));
+ assertEquals((Float) layer.getFillExtrusionHeight().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -459,26 +513,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-height");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionHeight(
- zoom(
- exponential(
- stop(2, fillExtrusionHeight(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -486,19 +542,21 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-height");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- fillExtrusionHeight(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -506,26 +564,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-height");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionHeight(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, fillExtrusionHeight(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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
@@ -533,29 +593,32 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-height");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionHeight(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, fillExtrusionHeight(0.3f))
- )
- ).withDefaultValue(fillExtrusionHeight(0.3f))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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());
}
@Test
@@ -563,34 +626,36 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-height");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -598,12 +663,14 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-baseTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillExtrusionBaseTransition(options);
- assertEquals(layer.getFillExtrusionBaseTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionBaseTransition(options);
+ assertEquals(layer.getFillExtrusionBaseTransition(), options);
+ });
}
@Test
@@ -611,11 +678,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-base");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillExtrusionBase(0.3f));
- assertEquals((Float) layer.getFillExtrusionBase().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(fillExtrusionBase(0.3f));
+ assertEquals((Float) layer.getFillExtrusionBase().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -623,26 +692,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-base");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionBase(
- zoom(
- exponential(
- stop(2, fillExtrusionBase(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -650,19 +721,21 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-base");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- fillExtrusionBase(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -670,26 +743,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-base");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionBase(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, fillExtrusionBase(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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
@@ -697,29 +772,32 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-base");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionBase(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, fillExtrusionBase(0.3f))
- )
- ).withDefaultValue(fillExtrusionBase(0.3f))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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
@@ -727,34 +805,36 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-base");
- 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);
+ invoke(mapboxMap, (uiController, 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);
+ });
}
}
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 b6b6578839..a5e7ea5fc9 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,14 +3,10 @@
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
-import android.support.test.espresso.Espresso;
-import android.support.test.rule.ActivityTestRule;
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.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.SourceFunction;
import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
@@ -19,26 +15,36 @@ 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.FillLayer;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
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 org.junit.Assert.*;
-import static com.mapbox.mapboxsdk.style.layers.Property.*;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+import timber.log.Timber;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+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.layers.Property.FILL_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+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;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOutlineColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillPattern;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* Basic smoke tests for FillLayer
@@ -53,15 +59,18 @@ public class FillLayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer(){
- 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");
- }
+ private void setupLayer() {
+ Timber.i("Retrieving 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");
+ }
+ });
}
@Test
@@ -69,14 +78,16 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- 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);
+ });
}
@Test
@@ -84,15 +95,17 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("SourceLayer");
- 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);
+ });
}
@Test
@@ -100,11 +113,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-antialias");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillAntialias(true));
- assertEquals((Boolean) layer.getFillAntialias().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(fillAntialias(true));
+ assertEquals((Boolean) layer.getFillAntialias().getValue(), (Boolean) true);
+ });
}
@Test
@@ -112,25 +127,27 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-antialias");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillAntialias(
- zoom(
- interval(
- stop(2, fillAntialias(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -138,12 +155,14 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-opacityTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillOpacityTransition(options);
- assertEquals(layer.getFillOpacityTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillOpacityTransition(options);
+ assertEquals(layer.getFillOpacityTransition(), options);
+ });
}
@Test
@@ -151,11 +170,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillOpacity(0.3f));
- assertEquals((Float) layer.getFillOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(fillOpacity(0.3f));
+ assertEquals((Float) layer.getFillOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -163,26 +184,28 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOpacity(
- zoom(
- exponential(
- stop(2, fillOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -190,19 +213,21 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- fillOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -210,26 +235,28 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, fillOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -237,29 +264,32 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOpacity(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, fillOpacity(0.3f))
- )
- ).withDefaultValue(fillOpacity(0.3f))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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());
}
@Test
@@ -267,34 +297,36 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-opacity");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -302,12 +334,14 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-colorTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillColorTransition(options);
- assertEquals(layer.getFillColorTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillColorTransition(options);
+ assertEquals(layer.getFillColorTransition(), options);
+ });
}
@Test
@@ -315,11 +349,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(fillColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -327,26 +363,28 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillColor(
- zoom(
- exponential(
- stop(2, fillColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -354,19 +392,21 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- fillColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -374,26 +414,28 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, fillColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -401,29 +443,32 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", fillColor(Color.RED))
- )
- ).withDefaultValue(fillColor(Color.GREEN))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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
@@ -431,11 +476,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillColor(Color.RED));
- assertEquals(layer.getFillColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(fillColor(Color.RED));
+ assertEquals(layer.getFillColorAsInt(), Color.RED);
+ });
}
@Test
@@ -443,12 +490,14 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-colorTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillOutlineColorTransition(options);
- assertEquals(layer.getFillOutlineColorTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillOutlineColorTransition(options);
+ assertEquals(layer.getFillOutlineColorTransition(), options);
+ });
}
@Test
@@ -456,11 +505,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(fillOutlineColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillOutlineColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -468,26 +519,28 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOutlineColor(
- zoom(
- exponential(
- stop(2, fillOutlineColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -495,19 +548,21 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- fillOutlineColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -515,26 +570,28 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOutlineColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, fillOutlineColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -542,29 +599,32 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOutlineColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", fillOutlineColor(Color.RED))
- )
- ).withDefaultValue(fillOutlineColor(Color.GREEN))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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
@@ -572,11 +632,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillOutlineColor(Color.RED));
- assertEquals(layer.getFillOutlineColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(fillOutlineColor(Color.RED));
+ assertEquals(layer.getFillOutlineColorAsInt(), Color.RED);
+ });
}
@Test
@@ -584,12 +646,14 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-translateTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillTranslateTransition(options);
- assertEquals(layer.getFillTranslateTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillTranslateTransition(options);
+ assertEquals(layer.getFillTranslateTransition(), options);
+ });
}
@Test
@@ -597,11 +661,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-translate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillTranslate(new Float[]{0f,0f}));
- assertEquals((Float[]) layer.getFillTranslate().getValue(), (Float[]) new Float[]{0f,0f});
+ // Set and Get
+ layer.setProperties(fillTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getFillTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
+ });
}
@Test
@@ -609,26 +675,28 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-translate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillTranslate(
- zoom(
- exponential(
- stop(2, fillTranslate(new Float[]{0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -636,11 +704,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-translate-anchor");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getFillTranslateAnchor().getValue(), (String) FILL_TRANSLATE_ANCHOR_MAP);
+ // Set and Get
+ layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getFillTranslateAnchor().getValue(), (String) FILL_TRANSLATE_ANCHOR_MAP);
+ });
}
@Test
@@ -648,25 +718,27 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-translate-anchor");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillTranslateAnchor(
- zoom(
- interval(
- stop(2, fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -674,12 +746,14 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-patternTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setFillPatternTransition(options);
- assertEquals(layer.getFillPatternTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillPatternTransition(options);
+ assertEquals(layer.getFillPatternTransition(), options);
+ });
}
@Test
@@ -687,11 +761,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-pattern");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(fillPattern("pedestrian-polygon"));
- assertEquals((String) layer.getFillPattern().getValue(), (String) "pedestrian-polygon");
+ // Set and Get
+ layer.setProperties(fillPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getFillPattern().getValue(), (String) "pedestrian-polygon");
+ });
}
@Test
@@ -699,25 +775,27 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-pattern");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillPattern(
- zoom(
- interval(
- stop(2, fillPattern("pedestrian-polygon"))
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
}
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 be2fc9ab9c..5d10cfa38a 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
@@ -1,6 +1,5 @@
package com.mapbox.mapboxsdk.testapp.style;
-import android.content.res.Resources;
import android.support.annotation.RawRes;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
@@ -13,6 +12,7 @@ import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
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;
@@ -21,18 +21,13 @@ import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
+
+import timber.log.Timber;
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.fail;
/**
* Tests for {@link GeoJsonSource}
@@ -46,19 +41,22 @@ public class GeoJsonSourceTests extends BaseActivityTest {
}
@Test
- public void testFeatureCollection() {
+ public void testFeatureCollection() throws Exception {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
- GeoJsonSource source = new GeoJsonSource("source", FeatureCollection
- .fromJson(readRawResource(rule.getActivity().getResources(), R.raw.test_feature_collection)));
+ GeoJsonSource source = null;
+ try {
+ source = new GeoJsonSource("source", FeatureCollection
+ .fromJson(ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_collection)));
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
mapboxMap.addSource(source);
-
mapboxMap.addLayer(new CircleLayer("layer", source.getId()));
}
-
});
}
@@ -79,14 +77,19 @@ public class GeoJsonSourceTests extends BaseActivityTest {
}
@Test
- public void testFeatureProperties() {
+ public void testFeatureProperties() throws IOException {
validateTestSetup();
onView(withId(R.id.mapView)).perform(new BaseViewAction() {
@Override
public void perform(UiController uiController, View view) {
- GeoJsonSource source = new GeoJsonSource("source",
- readRawResource(rule.getActivity().getResources(), R.raw.test_feature_properties));
+ GeoJsonSource source = null;
+ try {
+ source = new GeoJsonSource("source",
+ ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_properties));
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
mapboxMap.addSource(source);
mapboxMap.addLayer(new CircleLayer("layer", source.getId()));
@@ -141,8 +144,11 @@ public class GeoJsonSourceTests extends BaseActivityTest {
Layer layer = new CircleLayer("layer", source.getId());
mapboxMap.addLayer(layer);
- source.setGeoJson(Feature.fromJson(
- readRawResource(rule.getActivity().getResources(), resource)));
+ try {
+ source.setGeoJson(Feature.fromJson(ResourceUtils.readRawResource(rule.getActivity(), resource)));
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
mapboxMap.removeLayer(layer);
mapboxMap.removeSource(source);
@@ -151,27 +157,6 @@ public class GeoJsonSourceTests extends BaseActivityTest {
});
}
- private String readRawResource(Resources resources, @RawRes int rawResource) {
- InputStream is = resources.openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- try {
- 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();
- }
- } catch (IOException err) {
- fail(err.getMessage());
- }
-
- return writer.toString();
- }
-
public abstract class BaseViewAction implements ViewAction {
@Override
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
new file mode 100644
index 0000000000..c049fabb52
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java
@@ -0,0 +1,49 @@
+package com.mapbox.mapboxsdk.testapp.style;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.test.runner.AndroidJUnit4;
+
+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;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * CRUD tests around Image
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImageTest extends BaseActivityTest {
+
+ private static final String IMAGE_ID = "test.image";
+
+ @Override
+ protected Class getActivityClass() {
+ return RuntimeStyleTestActivity.class;
+ }
+
+ @Test
+ public void testAddGetImage() {
+ validateTestSetup();
+ 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 bitmapGet = mapboxMap.getImage(IMAGE_ID);
+ assertTrue(bitmapGet.sameAs(bitmapSet));
+
+ 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 36833fb4ee..21026527c5 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,22 +6,22 @@ import android.support.test.espresso.ViewAction;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
-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.layers.FillExtrusionLayer;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.light.Position;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
import com.mapbox.mapboxsdk.testapp.activity.style.FillExtrusionStyleTestActivity;
-import timber.log.Timber;
-
import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
+import timber.log.Timber;
+
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;
@@ -31,7 +31,7 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBas
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity;
-
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -43,89 +43,99 @@ public class LightTest extends BaseActivityTest {
@Test
public void testAnchor() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("anchor");
- 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());
+ });
}
@Test
public void testPositionTransition() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("positionTransitionOptions");
- 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());
+ });
}
@Test
public void testPosition() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("position");
- 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());
+ });
}
@Test
public void testColorTransition() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("colorTransitionOptions");
- 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());
+ });
}
@Test
public void testColor() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("color");
- 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());
+ });
}
@Test
public void testIntensityTransition() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("intensityTransitionOptions");
- 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());
+ });
}
@Test
public void testIntensity() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("intensity");
- 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());
+ });
}
- private void setupLayer() {
+ private void setupLight() {
onView(withId(R.id.mapView)).perform(new ViewAction() {
@Override
public Matcher<View> getConstraints() {
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 c10c908c8d..544c5505ed 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,14 +3,10 @@
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
-import android.support.test.espresso.Espresso;
-import android.support.test.rule.ActivityTestRule;
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.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.SourceFunction;
import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
@@ -19,26 +15,45 @@ 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.LineLayer;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
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 org.junit.Assert.*;
-import static com.mapbox.mapboxsdk.style.layers.Property.*;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+import timber.log.Timber;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+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.layers.Property.LINE_CAP_BUTT;
+import static com.mapbox.mapboxsdk.style.layers.Property.LINE_JOIN_BEVEL;
+import static com.mapbox.mapboxsdk.style.layers.Property.LINE_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineBlur;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineCap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineDasharray;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineGapWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineJoin;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineMiterLimit;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineOffset;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.linePattern;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineRoundLimit;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* Basic smoke tests for LineLayer
@@ -53,15 +68,18 @@ public class LineLayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer(){
- 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");
- }
+ private void setupLayer() {
+ Timber.i("Retrieving 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");
+ }
+ });
}
@Test
@@ -69,14 +87,16 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- 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);
+ });
}
@Test
@@ -84,15 +104,17 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("SourceLayer");
- 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);
+ });
}
@Test
@@ -100,11 +122,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-cap");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineCap(LINE_CAP_BUTT));
- assertEquals((String) layer.getLineCap().getValue(), (String) LINE_CAP_BUTT);
+ // Set and Get
+ layer.setProperties(lineCap(LINE_CAP_BUTT));
+ assertEquals((String) layer.getLineCap().getValue(), (String) LINE_CAP_BUTT);
+ });
}
@Test
@@ -112,25 +136,27 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-cap");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineCap(
- zoom(
- interval(
- stop(2, lineCap(LINE_CAP_BUTT))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -138,11 +164,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-join");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineJoin(LINE_JOIN_BEVEL));
- assertEquals((String) layer.getLineJoin().getValue(), (String) LINE_JOIN_BEVEL);
+ // Set and Get
+ layer.setProperties(lineJoin(LINE_JOIN_BEVEL));
+ assertEquals((String) layer.getLineJoin().getValue(), (String) LINE_JOIN_BEVEL);
+ });
}
@Test
@@ -150,25 +178,78 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-join");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineJoin(
- zoom(
- interval(
- stop(2, lineJoin(LINE_JOIN_BEVEL))
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testLineJoinAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("line-join");
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testLineJoinAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("line-join");
+ invoke(mapboxMap, (uiController, 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(CameraFunction.class, layer.getLineJoin().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getLineJoin().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getLineJoin().getFunction().getStops()).size());
+ // 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());
+ });
}
@Test
@@ -176,11 +257,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-miter-limit");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineMiterLimit(0.3f));
- assertEquals((Float) layer.getLineMiterLimit().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(lineMiterLimit(0.3f));
+ assertEquals((Float) layer.getLineMiterLimit().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -188,26 +271,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-miter-limit");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineMiterLimit(
- zoom(
- exponential(
- stop(2, lineMiterLimit(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -215,11 +300,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-round-limit");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineRoundLimit(0.3f));
- assertEquals((Float) layer.getLineRoundLimit().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(lineRoundLimit(0.3f));
+ assertEquals((Float) layer.getLineRoundLimit().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -227,26 +314,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-round-limit");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineRoundLimit(
- zoom(
- exponential(
- stop(2, lineRoundLimit(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -254,12 +343,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-opacityTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLineOpacityTransition(options);
- assertEquals(layer.getLineOpacityTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineOpacityTransition(options);
+ assertEquals(layer.getLineOpacityTransition(), options);
+ });
}
@Test
@@ -267,11 +358,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineOpacity(0.3f));
- assertEquals((Float) layer.getLineOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(lineOpacity(0.3f));
+ assertEquals((Float) layer.getLineOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -279,26 +372,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOpacity(
- zoom(
- exponential(
- stop(2, lineOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -306,19 +401,21 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- lineOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -326,26 +423,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -353,29 +452,32 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOpacity(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineOpacity(0.3f))
- )
- ).withDefaultValue(lineOpacity(0.3f))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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());
}
@Test
@@ -383,34 +485,36 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-opacity");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -418,12 +522,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-colorTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLineColorTransition(options);
- assertEquals(layer.getLineColorTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineColorTransition(options);
+ assertEquals(layer.getLineColorTransition(), options);
+ });
}
@Test
@@ -431,11 +537,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(lineColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getLineColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -443,26 +551,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineColor(
- zoom(
- exponential(
- stop(2, lineColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -470,19 +580,21 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- lineColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -490,26 +602,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, lineColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -517,29 +631,32 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", lineColor(Color.RED))
- )
- ).withDefaultValue(lineColor(Color.GREEN))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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
@@ -547,11 +664,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineColor(Color.RED));
- assertEquals(layer.getLineColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(lineColor(Color.RED));
+ assertEquals(layer.getLineColorAsInt(), Color.RED);
+ });
}
@Test
@@ -559,12 +678,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-translateTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLineTranslateTransition(options);
- assertEquals(layer.getLineTranslateTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineTranslateTransition(options);
+ assertEquals(layer.getLineTranslateTransition(), options);
+ });
}
@Test
@@ -572,11 +693,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-translate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineTranslate(new Float[]{0f,0f}));
- assertEquals((Float[]) layer.getLineTranslate().getValue(), (Float[]) new Float[]{0f,0f});
+ // Set and Get
+ layer.setProperties(lineTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getLineTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
+ });
}
@Test
@@ -584,26 +707,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-translate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineTranslate(
- zoom(
- exponential(
- stop(2, lineTranslate(new Float[]{0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -611,11 +736,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-translate-anchor");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineTranslateAnchor(LINE_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getLineTranslateAnchor().getValue(), (String) LINE_TRANSLATE_ANCHOR_MAP);
+ // Set and Get
+ layer.setProperties(lineTranslateAnchor(LINE_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getLineTranslateAnchor().getValue(), (String) LINE_TRANSLATE_ANCHOR_MAP);
+ });
}
@Test
@@ -623,25 +750,27 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-translate-anchor");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineTranslateAnchor(
- zoom(
- interval(
- stop(2, lineTranslateAnchor(LINE_TRANSLATE_ANCHOR_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -649,12 +778,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-widthTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLineWidthTransition(options);
- assertEquals(layer.getLineWidthTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineWidthTransition(options);
+ assertEquals(layer.getLineWidthTransition(), options);
+ });
}
@Test
@@ -662,11 +793,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineWidth(0.3f));
- assertEquals((Float) layer.getLineWidth().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(lineWidth(0.3f));
+ assertEquals((Float) layer.getLineWidth().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -674,26 +807,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineWidth(
- zoom(
- exponential(
- stop(2, lineWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -701,19 +836,21 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- lineWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -721,26 +858,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -748,29 +887,32 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineWidth(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineWidth(0.3f))
- )
- ).withDefaultValue(lineWidth(0.3f))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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
@@ -778,34 +920,36 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-width");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -813,12 +957,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-gap-widthTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLineGapWidthTransition(options);
- assertEquals(layer.getLineGapWidthTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineGapWidthTransition(options);
+ assertEquals(layer.getLineGapWidthTransition(), options);
+ });
}
@Test
@@ -826,11 +972,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-gap-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineGapWidth(0.3f));
- assertEquals((Float) layer.getLineGapWidth().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(lineGapWidth(0.3f));
+ assertEquals((Float) layer.getLineGapWidth().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -838,26 +986,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-gap-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineGapWidth(
- zoom(
- exponential(
- stop(2, lineGapWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -865,19 +1015,21 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-gap-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- lineGapWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -885,26 +1037,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-gap-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineGapWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineGapWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -912,29 +1066,32 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-gap-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineGapWidth(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineGapWidth(0.3f))
- )
- ).withDefaultValue(lineGapWidth(0.3f))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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
@@ -942,34 +1099,36 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-gap-width");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -977,12 +1136,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-offsetTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLineOffsetTransition(options);
- assertEquals(layer.getLineOffsetTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineOffsetTransition(options);
+ assertEquals(layer.getLineOffsetTransition(), options);
+ });
}
@Test
@@ -990,11 +1151,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-offset");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineOffset(0.3f));
- assertEquals((Float) layer.getLineOffset().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(lineOffset(0.3f));
+ assertEquals((Float) layer.getLineOffset().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1002,26 +1165,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-offset");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOffset(
- zoom(
- exponential(
- stop(2, lineOffset(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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
@@ -1029,19 +1194,21 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-offset");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- lineOffset(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -1049,26 +1216,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-offset");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOffset(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineOffset(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1076,29 +1245,32 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-offset");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOffset(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineOffset(0.3f))
- )
- ).withDefaultValue(lineOffset(0.3f))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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
@@ -1106,34 +1278,36 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-offset");
- 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);
+ invoke(mapboxMap, (uiController, 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);
+ });
}
@Test
@@ -1141,12 +1315,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-blurTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLineBlurTransition(options);
- assertEquals(layer.getLineBlurTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineBlurTransition(options);
+ assertEquals(layer.getLineBlurTransition(), options);
+ });
}
@Test
@@ -1154,11 +1330,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineBlur(0.3f));
- assertEquals((Float) layer.getLineBlur().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(lineBlur(0.3f));
+ assertEquals((Float) layer.getLineBlur().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1166,26 +1344,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineBlur(
- zoom(
- exponential(
- stop(2, lineBlur(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1193,19 +1373,21 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- lineBlur(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -1213,26 +1395,28 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineBlur(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineBlur(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1240,29 +1424,32 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineBlur(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineBlur(0.3f))
- )
- ).withDefaultValue(lineBlur(0.3f))
- )
- );
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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());
}
@Test
@@ -1270,34 +1457,36 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-blur");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -1305,12 +1494,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-dasharrayTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLineDasharrayTransition(options);
- assertEquals(layer.getLineDasharrayTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineDasharrayTransition(options);
+ assertEquals(layer.getLineDasharrayTransition(), options);
+ });
}
@Test
@@ -1318,11 +1509,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-dasharray");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(lineDasharray(new Float[]{}));
- assertEquals((Float[]) layer.getLineDasharray().getValue(), (Float[]) new Float[]{});
+ // Set and Get
+ layer.setProperties(lineDasharray(new Float[] {}));
+ assertEquals((Float[]) layer.getLineDasharray().getValue(), (Float[]) new Float[] {});
+ });
}
@Test
@@ -1330,25 +1523,27 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-dasharray");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineDasharray(
- zoom(
- interval(
- stop(2, lineDasharray(new Float[]{}))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1356,12 +1551,14 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-patternTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setLinePatternTransition(options);
- assertEquals(layer.getLinePatternTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLinePatternTransition(options);
+ assertEquals(layer.getLinePatternTransition(), options);
+ });
}
@Test
@@ -1369,11 +1566,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-pattern");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(linePattern("pedestrian-polygon"));
- assertEquals((String) layer.getLinePattern().getValue(), (String) "pedestrian-polygon");
+ // Set and Get
+ layer.setProperties(linePattern("pedestrian-polygon"));
+ assertEquals((String) layer.getLinePattern().getValue(), (String) "pedestrian-polygon");
+ });
}
@Test
@@ -1381,25 +1580,27 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-pattern");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- linePattern(
- zoom(
- interval(
- stop(2, linePattern("pedestrian-polygon"))
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
}
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 2a0d3401fb..11368767fe 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
@@ -2,43 +2,36 @@
package com.mapbox.mapboxsdk.testapp.style;
-import android.graphics.Color;
-import android.support.test.espresso.Espresso;
-import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
-import timber.log.Timber;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.style.functions.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.CameraFunction;
-import com.mapbox.mapboxsdk.style.functions.SourceFunction;
-import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
-import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
-import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops;
-import com.mapbox.mapboxsdk.style.functions.stops.Stop;
-import com.mapbox.mapboxsdk.style.functions.stops.Stops;
import com.mapbox.mapboxsdk.style.layers.RasterLayer;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
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 org.junit.Assert.*;
-import static com.mapbox.mapboxsdk.style.layers.Property.*;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+import timber.log.Timber;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+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.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterBrightnessMax;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterBrightnessMin;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterContrast;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterFadeDuration;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterHueRotate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.rasterSaturation;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* Basic smoke tests for RasterLayer
@@ -53,15 +46,18 @@ public class RasterLayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer(){
- 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");
- }
+ private void setupLayer() {
+ Timber.i("Retrieving 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");
+ }
+ });
}
@Test
@@ -69,14 +65,16 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- 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);
+ });
}
@Test
@@ -84,12 +82,14 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-opacityTransitionOptions");
- 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);
+ });
}
@Test
@@ -97,11 +97,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(rasterOpacity(0.3f));
- assertEquals((Float) layer.getRasterOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(rasterOpacity(0.3f));
+ assertEquals((Float) layer.getRasterOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -109,26 +111,28 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterOpacity(
- zoom(
- exponential(
- stop(2, rasterOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -136,12 +140,14 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-hue-rotateTransitionOptions");
- 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);
+ });
}
@Test
@@ -149,11 +155,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-hue-rotate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(rasterHueRotate(0.3f));
- assertEquals((Float) layer.getRasterHueRotate().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(rasterHueRotate(0.3f));
+ assertEquals((Float) layer.getRasterHueRotate().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -161,26 +169,28 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-hue-rotate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterHueRotate(
- zoom(
- exponential(
- stop(2, rasterHueRotate(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -188,12 +198,14 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-minTransitionOptions");
- 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);
+ });
}
@Test
@@ -201,11 +213,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-min");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(rasterBrightnessMin(0.3f));
- assertEquals((Float) layer.getRasterBrightnessMin().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(rasterBrightnessMin(0.3f));
+ assertEquals((Float) layer.getRasterBrightnessMin().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -213,26 +227,28 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-min");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterBrightnessMin(
- zoom(
- exponential(
- stop(2, rasterBrightnessMin(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -240,12 +256,14 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-maxTransitionOptions");
- 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);
+ });
}
@Test
@@ -253,11 +271,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-max");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(rasterBrightnessMax(0.3f));
- assertEquals((Float) layer.getRasterBrightnessMax().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(rasterBrightnessMax(0.3f));
+ assertEquals((Float) layer.getRasterBrightnessMax().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -265,26 +285,28 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-max");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterBrightnessMax(
- zoom(
- exponential(
- stop(2, rasterBrightnessMax(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -292,12 +314,14 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-saturationTransitionOptions");
- 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);
+ });
}
@Test
@@ -305,11 +329,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-saturation");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(rasterSaturation(0.3f));
- assertEquals((Float) layer.getRasterSaturation().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(rasterSaturation(0.3f));
+ assertEquals((Float) layer.getRasterSaturation().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -317,26 +343,28 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-saturation");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterSaturation(
- zoom(
- exponential(
- stop(2, rasterSaturation(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -344,12 +372,14 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-contrastTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setRasterContrastTransition(options);
- assertEquals(layer.getRasterContrastTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setRasterContrastTransition(options);
+ assertEquals(layer.getRasterContrastTransition(), options);
+ });
}
@Test
@@ -357,11 +387,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-contrast");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(rasterContrast(0.3f));
- assertEquals((Float) layer.getRasterContrast().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(rasterContrast(0.3f));
+ assertEquals((Float) layer.getRasterContrast().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -369,26 +401,28 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-contrast");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterContrast(
- zoom(
- exponential(
- stop(2, rasterContrast(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
@Test
@@ -396,12 +430,14 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-fade-durationTransitionOptions");
- assertNotNull(layer);
-
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setRasterFadeDurationTransition(options);
- assertEquals(layer.getRasterFadeDurationTransition(), options);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setRasterFadeDurationTransition(options);
+ assertEquals(layer.getRasterFadeDurationTransition(), options);
+ });
}
@Test
@@ -409,11 +445,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-fade-duration");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(rasterFadeDuration(0.3f));
- assertEquals((Float) layer.getRasterFadeDuration().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(rasterFadeDuration(0.3f));
+ assertEquals((Float) layer.getRasterFadeDuration().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -421,26 +459,28 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-fade-duration");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterFadeDuration(
- zoom(
- exponential(
- stop(2, rasterFadeDuration(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleBackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleBackgroundLayerTest.java
deleted file mode 100644
index c95c959644..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleBackgroundLayerTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.style;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.ActivityInstrumentationTestCase2;
-
-import timber.log.Timber;
-
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.style.layers.BackgroundLayer;
-import com.mapbox.mapboxsdk.style.layers.Property;
-import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Basic smoke tests for BackgroundLayer
- */
-@RunWith(AndroidJUnit4.class)
-public class RuntimeStyleBackgroundLayerTest
- extends ActivityInstrumentationTestCase2<RuntimeStyleTestActivity> {
-
- public RuntimeStyleBackgroundLayerTest() {
- super(RuntimeStyleTestActivity.class);
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
- injectInstrumentation(InstrumentationRegistry.getInstrumentation());
- }
-
- @Test
- public void testSetVisibility() {
- getActivity().mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- Timber.i("visibility");
- BackgroundLayer layer = mapboxMap.getLayerAs("background");
- assertNotNull(layer);
-
- // Get initial
- assertEquals(layer.getVisibility().getValue(), Property.VISIBLE);
-
- // Set
- layer.setProperties(PropertyFactory.visibility(Property.NONE));
- assertEquals(layer.getVisibility().getValue(), Property.NONE);
- }
- });
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-}
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 f1e2a6c418..fc526176d4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java
@@ -40,6 +40,7 @@ import timber.log.Timber;
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.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -189,63 +190,76 @@ public class RuntimeStyleTests extends BaseActivityTest {
@Test
public void testAddRemoveSource() {
validateTestSetup();
- 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");
- onView(withId(R.id.mapView)).perform(new AddRemoveSourceAction());
- }
+ // Add initial source
+ mapboxMap.addSource(new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2"));
- @Test
- public void testVectorSourceUrlGetter() {
- validateTestSetup();
+ // Remove
+ Source mySource = mapboxMap.removeSource("my-source");
+ assertNotNull(mySource);
+ assertNull(mapboxMap.getLayer("my-source"));
- onView(withId(R.id.mapView)).perform(new BaseViewAction() {
+ // Add
+ Source source = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
+ mapboxMap.addSource(source);
- @Override
- public void perform(UiController uiController, View view) {
- VectorSource source = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
- mapboxMap.addSource(source);
- assertEquals("mapbox://mapbox.mapbox-terrain-v2", source.getUrl());
+ // Remove, preserving the reference
+ mapboxMap.removeSource(source);
+
+ // Re-add the reference...
+ mapboxMap.addSource(source);
+
+ // 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
+ public void testVectorSourceUrlGetter() {
+ validateTestSetup();
+ 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();
-
- onView(withId(R.id.mapView)).perform(new BaseViewAction() {
-
- @Override
- public void perform(UiController uiController, View view) {
- 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() {
+ public void testGeoJsonSourceUrlGetter() throws MalformedURLException {
validateTestSetup();
-
- onView(withId(R.id.mapView)).perform(new BaseViewAction() {
-
- @Override
- public void perform(UiController uiController, View view) {
- 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 err) {
- assertTrue(err.getMessage(), false);
- }
- 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());
});
}
@@ -329,42 +343,6 @@ public class RuntimeStyleTests extends BaseActivityTest {
}
}
- private class AddRemoveSourceAction extends BaseViewAction {
-
- @Override
- public void perform(UiController uiController, View view) {
- // 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"));
-
- // Add
- Source source = new VectorSource("my-source", "mapbox://mapbox.mapbox-terrain-v2");
- mapboxMap.addSource(source);
-
- // Remove, preserving the reference
- mapboxMap.removeSource(source);
-
- // Re-add the reference...
- mapboxMap.addSource(source);
-
- // 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
- }
- }
- }
-
@After
public void unregisterIntentServiceIdlingResource() {
Espresso.unregisterIdlingResources(idlingResource);
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 737a66713a..cb79700430 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,14 +3,10 @@
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
-import android.support.test.espresso.Espresso;
-import android.support.test.rule.ActivityTestRule;
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.CompositeFunction;
import com.mapbox.mapboxsdk.style.functions.SourceFunction;
import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
@@ -19,26 +15,90 @@ 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.SymbolLayer;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
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 org.junit.Assert.*;
-import static com.mapbox.mapboxsdk.style.layers.Property.*;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+import timber.log.Timber;
-import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+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.layers.Property.ICON_ANCHOR_CENTER;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_PITCH_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ROTATION_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_TEXT_FIT_NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.SYMBOL_PLACEMENT_POINT;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_ANCHOR_CENTER;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_JUSTIFY_LEFT;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_PITCH_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_ROTATION_ALIGNMENT_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_TRANSFORM_NONE;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_TRANSLATE_ANCHOR_MAP;
+import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+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.iconColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconHaloBlur;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconHaloColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconHaloWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconKeepUpright;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOptional;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconPadding;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconPitchAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotationAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTextFit;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTextFitPadding;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.symbolAvoidEdges;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.symbolPlacement;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.symbolSpacing;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap;
+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.textFont;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textHaloBlur;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textHaloColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textHaloWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textIgnorePlacement;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textJustify;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textKeepUpright;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textLetterSpacing;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textLineHeight;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textMaxAngle;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textMaxWidth;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOffset;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textOptional;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textPadding;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textPitchAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textRotate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textRotationAlignment;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textTransform;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textTranslate;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textTranslateAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
/**
* Basic smoke tests for SymbolLayer
@@ -53,15 +113,18 @@ public class SymbolLayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer(){
- 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");
- }
+ private void setupLayer() {
+ Timber.i("Retrieving 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");
+ }
+ });
}
@Test
@@ -69,14 +132,16 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- 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);
+ });
}
@Test
@@ -84,15 +149,17 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("SourceLayer");
- 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);
+ });
}
@Test
@@ -100,11 +167,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("symbol-placement");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(symbolPlacement(SYMBOL_PLACEMENT_POINT));
- assertEquals((String) layer.getSymbolPlacement().getValue(), (String) SYMBOL_PLACEMENT_POINT);
+ // Set and Get
+ layer.setProperties(symbolPlacement(SYMBOL_PLACEMENT_POINT));
+ assertEquals((String) layer.getSymbolPlacement().getValue(), (String) SYMBOL_PLACEMENT_POINT);
+ });
}
@Test
@@ -112,25 +181,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("symbol-placement");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- symbolPlacement(
- zoom(
- interval(
- stop(2, symbolPlacement(SYMBOL_PLACEMENT_POINT))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -138,11 +209,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("symbol-spacing");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(symbolSpacing(0.3f));
- assertEquals((Float) layer.getSymbolSpacing().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(symbolSpacing(0.3f));
+ assertEquals((Float) layer.getSymbolSpacing().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -150,26 +223,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("symbol-spacing");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- symbolSpacing(
- zoom(
- exponential(
- stop(2, symbolSpacing(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -177,11 +252,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("symbol-avoid-edges");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(symbolAvoidEdges(true));
- assertEquals((Boolean) layer.getSymbolAvoidEdges().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(symbolAvoidEdges(true));
+ assertEquals((Boolean) layer.getSymbolAvoidEdges().getValue(), (Boolean) true);
+ });
}
@Test
@@ -189,25 +266,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("symbol-avoid-edges");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- symbolAvoidEdges(
- zoom(
- interval(
- stop(2, symbolAvoidEdges(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -215,11 +294,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-allow-overlap");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconAllowOverlap(true));
- assertEquals((Boolean) layer.getIconAllowOverlap().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(iconAllowOverlap(true));
+ assertEquals((Boolean) layer.getIconAllowOverlap().getValue(), (Boolean) true);
+ });
}
@Test
@@ -227,25 +308,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-allow-overlap");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconAllowOverlap(
- zoom(
- interval(
- stop(2, iconAllowOverlap(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -253,11 +336,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-ignore-placement");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconIgnorePlacement(true));
- assertEquals((Boolean) layer.getIconIgnorePlacement().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(iconIgnorePlacement(true));
+ assertEquals((Boolean) layer.getIconIgnorePlacement().getValue(), (Boolean) true);
+ });
}
@Test
@@ -265,25 +350,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-ignore-placement");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconIgnorePlacement(
- zoom(
- interval(
- stop(2, iconIgnorePlacement(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -291,11 +378,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-optional");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconOptional(true));
- assertEquals((Boolean) layer.getIconOptional().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(iconOptional(true));
+ assertEquals((Boolean) layer.getIconOptional().getValue(), (Boolean) true);
+ });
}
@Test
@@ -303,25 +392,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-optional");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOptional(
- zoom(
- interval(
- stop(2, iconOptional(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -329,11 +420,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotation-alignment");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP));
- assertEquals((String) layer.getIconRotationAlignment().getValue(), (String) ICON_ROTATION_ALIGNMENT_MAP);
+ // Set and Get
+ layer.setProperties(iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP));
+ assertEquals((String) layer.getIconRotationAlignment().getValue(), (String) ICON_ROTATION_ALIGNMENT_MAP);
+ });
}
@Test
@@ -341,25 +434,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotation-alignment");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotationAlignment(
- zoom(
- interval(
- stop(2, iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -367,11 +462,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-size");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconSize(0.3f));
- assertEquals((Float) layer.getIconSize().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(iconSize(0.3f));
+ assertEquals((Float) layer.getIconSize().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -379,26 +476,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-size");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconSize(
- zoom(
- exponential(
- stop(2, iconSize(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -406,19 +505,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-size");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconSize(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -426,26 +527,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-size");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconSize(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconSize(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -453,29 +556,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-size");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconSize(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, iconSize(0.3f))
+ )
+ ).withDefaultValue(iconSize(0.3f))
+ )
+ );
- // 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());
+ });
- // 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());
}
@Test
@@ -483,34 +589,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-size");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -518,11 +626,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-text-fit");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconTextFit(ICON_TEXT_FIT_NONE));
- assertEquals((String) layer.getIconTextFit().getValue(), (String) ICON_TEXT_FIT_NONE);
+ // Set and Get
+ layer.setProperties(iconTextFit(ICON_TEXT_FIT_NONE));
+ assertEquals((String) layer.getIconTextFit().getValue(), (String) ICON_TEXT_FIT_NONE);
+ });
}
@Test
@@ -530,25 +640,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-text-fit");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconTextFit(
- zoom(
- interval(
- stop(2, iconTextFit(ICON_TEXT_FIT_NONE))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -556,11 +668,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-text-fit-padding");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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});
+ // Set and Get
+ layer.setProperties(iconTextFitPadding(new Float[] {0f, 0f, 0f, 0f}));
+ assertEquals((Float[]) layer.getIconTextFitPadding().getValue(), (Float[]) new Float[] {0f, 0f, 0f, 0f});
+ });
}
@Test
@@ -568,26 +682,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-text-fit-padding");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconTextFitPadding(
- zoom(
- exponential(
- stop(2, iconTextFitPadding(new Float[]{0f,0f,0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -595,11 +711,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-image");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconImage("undefined"));
- assertEquals((String) layer.getIconImage().getValue(), (String) "undefined");
+ // Set and Get
+ layer.setProperties(iconImage("undefined"));
+ assertEquals((String) layer.getIconImage().getValue(), (String) "undefined");
+ });
}
@Test
@@ -607,25 +725,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-image");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconImage(
- zoom(
- interval(
- stop(2, iconImage("undefined"))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -633,19 +753,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-image");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconImage(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -653,26 +775,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-image");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconImage(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, iconImage("undefined"))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -680,11 +804,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconRotate(0.3f));
- assertEquals((Float) layer.getIconRotate().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(iconRotate(0.3f));
+ assertEquals((Float) layer.getIconRotate().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -692,26 +818,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotate(
- zoom(
- exponential(
- stop(2, iconRotate(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -719,19 +847,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconRotate(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -739,26 +869,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotate(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconRotate(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -766,29 +898,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconRotate(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, iconRotate(0.3f))
+ )
+ ).withDefaultValue(iconRotate(0.3f))
+ )
+ );
- // 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());
+ });
- // 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());
}
@Test
@@ -796,34 +931,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotate");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -831,11 +968,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-padding");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconPadding(0.3f));
- assertEquals((Float) layer.getIconPadding().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(iconPadding(0.3f));
+ assertEquals((Float) layer.getIconPadding().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -843,26 +982,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-padding");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconPadding(
- zoom(
- exponential(
- stop(2, iconPadding(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -870,11 +1011,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-keep-upright");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconKeepUpright(true));
- assertEquals((Boolean) layer.getIconKeepUpright().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(iconKeepUpright(true));
+ assertEquals((Boolean) layer.getIconKeepUpright().getValue(), (Boolean) true);
+ });
}
@Test
@@ -882,25 +1025,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-keep-upright");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconKeepUpright(
- zoom(
- interval(
- stop(2, iconKeepUpright(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -908,11 +1053,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-offset");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconOffset(new Float[]{0f,0f}));
- assertEquals((Float[]) layer.getIconOffset().getValue(), (Float[]) new Float[]{0f,0f});
+ // Set and Get
+ layer.setProperties(iconOffset(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getIconOffset().getValue(), (Float[]) new Float[] {0f, 0f});
+ });
}
@Test
@@ -920,26 +1067,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-offset");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOffset(
- zoom(
- exponential(
- stop(2, iconOffset(new Float[]{0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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
@@ -947,19 +1096,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-offset");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconOffset(property("FeaturePropertyA", Stops.<Float[]>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -967,26 +1118,163 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-offset");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(iconAnchor(ICON_ANCHOR_CENTER));
+ assertEquals((String) layer.getIconAnchor().getValue(), (String) ICON_ANCHOR_CENTER);
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testIconAnchorAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-anchor");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconAnchor(
+ property(
+ "FeaturePropertyA",
+ interval(
+ stop(1, iconAnchor(ICON_ANCHOR_CENTER))
+ )
+ )
+ )
+ );
- // Set
- layer.setProperties(
- iconOffset(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, iconOffset(new Float[]{0f,0f}))
+ // 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());
+ });
+ }
+
+ @Test
+ public void testIconPitchAlignmentAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-pitch-alignment");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getIconPitchAlignment().getValue(), (String) ICON_PITCH_ALIGNMENT_MAP);
+ });
+ }
+
+ @Test
+ public void testIconPitchAlignmentAsCameraFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("icon-pitch-alignment");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconPitchAlignment(
+ zoom(
+ interval(
+ stop(2, iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP))
+ )
)
)
- )
- );
+ );
- // 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());
+ // 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());
+ });
}
@Test
@@ -994,11 +1282,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-pitch-alignment");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textPitchAlignment(TEXT_PITCH_ALIGNMENT_MAP));
- assertEquals((String) layer.getTextPitchAlignment().getValue(), (String) TEXT_PITCH_ALIGNMENT_MAP);
+ // Set and Get
+ layer.setProperties(textPitchAlignment(TEXT_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getTextPitchAlignment().getValue(), (String) TEXT_PITCH_ALIGNMENT_MAP);
+ });
}
@Test
@@ -1006,25 +1296,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-pitch-alignment");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textPitchAlignment(
- zoom(
- interval(
- stop(2, textPitchAlignment(TEXT_PITCH_ALIGNMENT_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1032,11 +1324,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotation-alignment");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textRotationAlignment(TEXT_ROTATION_ALIGNMENT_MAP));
- assertEquals((String) layer.getTextRotationAlignment().getValue(), (String) TEXT_ROTATION_ALIGNMENT_MAP);
+ // Set and Get
+ layer.setProperties(textRotationAlignment(TEXT_ROTATION_ALIGNMENT_MAP));
+ assertEquals((String) layer.getTextRotationAlignment().getValue(), (String) TEXT_ROTATION_ALIGNMENT_MAP);
+ });
}
@Test
@@ -1044,25 +1338,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotation-alignment");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotationAlignment(
- zoom(
- interval(
- stop(2, textRotationAlignment(TEXT_ROTATION_ALIGNMENT_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1070,11 +1366,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-field");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textField(""));
- assertEquals((String) layer.getTextField().getValue(), (String) "");
+ // Set and Get
+ layer.setProperties(textField(""));
+ assertEquals((String) layer.getTextField().getValue(), (String) "");
+ });
}
@Test
@@ -1082,25 +1380,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-field");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textField(
- zoom(
- interval(
- stop(2, textField(""))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1108,19 +1408,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-field");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textField(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -1128,26 +1430,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-field");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textField(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, textField(""))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1155,11 +1459,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-font");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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"});
+ // 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"});
+ });
}
@Test
@@ -1167,25 +1473,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-font");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textFont(
- zoom(
- interval(
- stop(2, textFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1193,11 +1501,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-size");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textSize(0.3f));
- assertEquals((Float) layer.getTextSize().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textSize(0.3f));
+ assertEquals((Float) layer.getTextSize().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1205,26 +1515,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-size");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textSize(
- zoom(
- exponential(
- stop(2, textSize(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1232,19 +1544,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-size");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textSize(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -1252,26 +1566,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-size");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textSize(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textSize(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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
@@ -1279,29 +1595,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-size");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textSize(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textSize(0.3f))
+ )
+ ).withDefaultValue(textSize(0.3f))
+ )
+ );
- // 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());
+ });
- // 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());
}
@Test
@@ -1309,34 +1628,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-size");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -1344,11 +1665,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-max-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textMaxWidth(0.3f));
- assertEquals((Float) layer.getTextMaxWidth().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textMaxWidth(0.3f));
+ assertEquals((Float) layer.getTextMaxWidth().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1356,26 +1679,149 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-max-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testTextMaxWidthAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textMaxWidth(
- zoom(
- exponential(
- stop(2, textMaxWidth(0.3f))
- ).withBase(0.5f)
+ // 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());
+ });
+ }
+
+ @Test
+ public void testTextMaxWidthAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testTextMaxWidthAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, (uiController, 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());
+ });
- // 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());
+ }
+
+ @Test
+ public void testTextMaxWidthAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-max-width");
+ invoke(mapboxMap, (uiController, 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
@@ -1383,11 +1829,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-line-height");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textLineHeight(0.3f));
- assertEquals((Float) layer.getTextLineHeight().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textLineHeight(0.3f));
+ assertEquals((Float) layer.getTextLineHeight().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1395,26 +1843,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-line-height");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textLineHeight(
- zoom(
- exponential(
- stop(2, textLineHeight(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1422,11 +1872,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-letter-spacing");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textLetterSpacing(0.3f));
- assertEquals((Float) layer.getTextLetterSpacing().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textLetterSpacing(0.3f));
+ assertEquals((Float) layer.getTextLetterSpacing().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1434,26 +1886,149 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-letter-spacing");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testTextLetterSpacingAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testTextLetterSpacingAsExponentialSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testTextLetterSpacingAsCategoricalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, (uiController, 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
- layer.setProperties(
- textLetterSpacing(
- zoom(
- exponential(
- stop(2, textLetterSpacing(0.3f))
- ).withBase(0.5f)
+ }
+
+ @Test
+ public void testTextLetterSpacingAsCompositeFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-letter-spacing");
+ invoke(mapboxMap, (uiController, 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(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());
+ // 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
@@ -1461,11 +2036,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-justify");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textJustify(TEXT_JUSTIFY_LEFT));
- assertEquals((String) layer.getTextJustify().getValue(), (String) TEXT_JUSTIFY_LEFT);
+ // Set and Get
+ layer.setProperties(textJustify(TEXT_JUSTIFY_LEFT));
+ assertEquals((String) layer.getTextJustify().getValue(), (String) TEXT_JUSTIFY_LEFT);
+ });
}
@Test
@@ -1473,25 +2050,78 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-justify");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testTextJustifyAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-justify");
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
- // Set
- layer.setProperties(
- textJustify(
- zoom(
- interval(
- stop(2, textJustify(TEXT_JUSTIFY_LEFT))
+ @Test
+ public void testTextJustifyAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-justify");
+ invoke(mapboxMap, (uiController, 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(CameraFunction.class, layer.getTextJustify().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextJustify().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextJustify().getFunction().getStops()).size());
+ // 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());
+ });
}
@Test
@@ -1499,11 +2129,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-anchor");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textAnchor(TEXT_ANCHOR_CENTER));
- assertEquals((String) layer.getTextAnchor().getValue(), (String) TEXT_ANCHOR_CENTER);
+ // Set and Get
+ layer.setProperties(textAnchor(TEXT_ANCHOR_CENTER));
+ assertEquals((String) layer.getTextAnchor().getValue(), (String) TEXT_ANCHOR_CENTER);
+ });
}
@Test
@@ -1511,25 +2143,78 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-anchor");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textAnchor(
+ zoom(
+ interval(
+ stop(2, textAnchor(TEXT_ANCHOR_CENTER))
+ )
+ )
+ )
+ );
- // 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());
+ });
+ }
+
+ @Test
+ public void testTextAnchorAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-anchor");
+ invoke(mapboxMap, (uiController, 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());
+ });
+ }
+
+ @Test
+ public void testTextAnchorAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-anchor");
+ invoke(mapboxMap, (uiController, 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(CameraFunction.class, layer.getTextAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextAnchor().getFunction().getStops()).size());
+ // 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());
+ });
}
@Test
@@ -1537,11 +2222,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-max-angle");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textMaxAngle(0.3f));
- assertEquals((Float) layer.getTextMaxAngle().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textMaxAngle(0.3f));
+ assertEquals((Float) layer.getTextMaxAngle().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1549,26 +2236,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-max-angle");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textMaxAngle(
- zoom(
- exponential(
- stop(2, textMaxAngle(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1576,11 +2265,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textRotate(0.3f));
- assertEquals((Float) layer.getTextRotate().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textRotate(0.3f));
+ assertEquals((Float) layer.getTextRotate().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1588,26 +2279,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotate(
- zoom(
- exponential(
- stop(2, textRotate(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1615,19 +2308,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textRotate(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -1635,26 +2330,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotate(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textRotate(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1662,29 +2359,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textRotate(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textRotate(0.3f))
+ )
+ ).withDefaultValue(textRotate(0.3f))
+ )
+ );
- // 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());
+ });
- // 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
@@ -1692,34 +2392,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotate");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -1727,11 +2429,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-padding");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textPadding(0.3f));
- assertEquals((Float) layer.getTextPadding().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textPadding(0.3f));
+ assertEquals((Float) layer.getTextPadding().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -1739,26 +2443,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-padding");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textPadding(
- zoom(
- exponential(
- stop(2, textPadding(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1766,11 +2472,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-keep-upright");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textKeepUpright(true));
- assertEquals((Boolean) layer.getTextKeepUpright().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(textKeepUpright(true));
+ assertEquals((Boolean) layer.getTextKeepUpright().getValue(), (Boolean) true);
+ });
}
@Test
@@ -1778,25 +2486,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-keep-upright");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textKeepUpright(
- zoom(
- interval(
- stop(2, textKeepUpright(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1804,11 +2514,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-transform");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textTransform(TEXT_TRANSFORM_NONE));
- assertEquals((String) layer.getTextTransform().getValue(), (String) TEXT_TRANSFORM_NONE);
+ // Set and Get
+ layer.setProperties(textTransform(TEXT_TRANSFORM_NONE));
+ assertEquals((String) layer.getTextTransform().getValue(), (String) TEXT_TRANSFORM_NONE);
+ });
}
@Test
@@ -1816,25 +2528,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-transform");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTransform(
- zoom(
- interval(
- stop(2, textTransform(TEXT_TRANSFORM_NONE))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1842,19 +2556,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-transform");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textTransform(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -1862,26 +2578,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-transform");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTransform(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, textTransform(TEXT_TRANSFORM_NONE))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1889,11 +2607,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-offset");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textOffset(new Float[]{0f,0f}));
- assertEquals((Float[]) layer.getTextOffset().getValue(), (Float[]) new Float[]{0f,0f});
+ // Set and Get
+ layer.setProperties(textOffset(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getTextOffset().getValue(), (Float[]) new Float[] {0f, 0f});
+ });
}
@Test
@@ -1901,26 +2621,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-offset");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOffset(
- zoom(
- exponential(
- stop(2, textOffset(new Float[]{0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1928,19 +2650,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-offset");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textOffset(property("FeaturePropertyA", Stops.<Float[]>identity()))
- );
+ // 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());
+ // 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
@@ -1948,26 +2672,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-offset");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOffset(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, textOffset(new Float[]{0f,0f}))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -1975,11 +2701,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-allow-overlap");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textAllowOverlap(true));
- assertEquals((Boolean) layer.getTextAllowOverlap().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(textAllowOverlap(true));
+ assertEquals((Boolean) layer.getTextAllowOverlap().getValue(), (Boolean) true);
+ });
}
@Test
@@ -1987,25 +2715,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-allow-overlap");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textAllowOverlap(
- zoom(
- interval(
- stop(2, textAllowOverlap(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2013,11 +2743,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-ignore-placement");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textIgnorePlacement(true));
- assertEquals((Boolean) layer.getTextIgnorePlacement().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(textIgnorePlacement(true));
+ assertEquals((Boolean) layer.getTextIgnorePlacement().getValue(), (Boolean) true);
+ });
}
@Test
@@ -2025,25 +2757,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-ignore-placement");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textIgnorePlacement(
- zoom(
- interval(
- stop(2, textIgnorePlacement(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2051,11 +2785,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-optional");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textOptional(true));
- assertEquals((Boolean) layer.getTextOptional().getValue(), (Boolean) true);
+ // Set and Get
+ layer.setProperties(textOptional(true));
+ assertEquals((Boolean) layer.getTextOptional().getValue(), (Boolean) true);
+ });
}
@Test
@@ -2063,25 +2799,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-optional");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOptional(
- zoom(
- interval(
- stop(2, textOptional(true))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2089,12 +2827,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-opacityTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setIconOpacityTransition(options);
- assertEquals(layer.getIconOpacityTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconOpacityTransition(options);
+ assertEquals(layer.getIconOpacityTransition(), options);
+ });
}
@Test
@@ -2102,11 +2842,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconOpacity(0.3f));
- assertEquals((Float) layer.getIconOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(iconOpacity(0.3f));
+ assertEquals((Float) layer.getIconOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -2114,26 +2856,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOpacity(
- zoom(
- exponential(
- stop(2, iconOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2141,19 +2885,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -2161,26 +2907,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2188,29 +2936,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconOpacity(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, iconOpacity(0.3f))
+ )
+ ).withDefaultValue(iconOpacity(0.3f))
+ )
+ );
- // 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());
+ });
- // 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
@@ -2218,34 +2969,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-opacity");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -2253,12 +3006,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-colorTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setIconColorTransition(options);
- assertEquals(layer.getIconColorTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconColorTransition(options);
+ assertEquals(layer.getIconColorTransition(), options);
+ });
}
@Test
@@ -2266,11 +3021,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(iconColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getIconColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -2278,26 +3035,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconColor(
- zoom(
- exponential(
- stop(2, iconColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2305,19 +3064,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -2325,26 +3086,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, iconColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2352,29 +3115,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconColor(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop("valueA", iconColor(Color.RED))
+ )
+ ).withDefaultValue(iconColor(Color.GREEN))
+ )
+ );
- // 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());
+ });
- // 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
@@ -2382,11 +3148,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconColor(Color.RED));
- assertEquals(layer.getIconColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(iconColor(Color.RED));
+ assertEquals(layer.getIconColorAsInt(), Color.RED);
+ });
}
@Test
@@ -2394,12 +3162,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-colorTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setIconHaloColorTransition(options);
- assertEquals(layer.getIconHaloColorTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconHaloColorTransition(options);
+ assertEquals(layer.getIconHaloColorTransition(), options);
+ });
}
@Test
@@ -2407,11 +3177,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(iconHaloColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getIconHaloColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -2419,26 +3191,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloColor(
- zoom(
- exponential(
- stop(2, iconHaloColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2446,19 +3220,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconHaloColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -2466,26 +3242,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, iconHaloColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2493,29 +3271,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconHaloColor(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop("valueA", iconHaloColor(Color.RED))
+ )
+ ).withDefaultValue(iconHaloColor(Color.GREEN))
+ )
+ );
- // 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());
+ });
- // 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
@@ -2523,11 +3304,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconHaloColor(Color.RED));
- assertEquals(layer.getIconHaloColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(iconHaloColor(Color.RED));
+ assertEquals(layer.getIconHaloColorAsInt(), Color.RED);
+ });
}
@Test
@@ -2535,12 +3318,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-widthTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setIconHaloWidthTransition(options);
- assertEquals(layer.getIconHaloWidthTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconHaloWidthTransition(options);
+ assertEquals(layer.getIconHaloWidthTransition(), options);
+ });
}
@Test
@@ -2548,11 +3333,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconHaloWidth(0.3f));
- assertEquals((Float) layer.getIconHaloWidth().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(iconHaloWidth(0.3f));
+ assertEquals((Float) layer.getIconHaloWidth().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -2560,26 +3347,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloWidth(
- zoom(
- exponential(
- stop(2, iconHaloWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2587,19 +3376,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconHaloWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -2607,26 +3398,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconHaloWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2634,29 +3427,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconHaloWidth(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, iconHaloWidth(0.3f))
+ )
+ ).withDefaultValue(iconHaloWidth(0.3f))
+ )
+ );
- // 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());
+ });
- // 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());
}
@Test
@@ -2664,34 +3460,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-width");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -2699,12 +3497,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blurTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setIconHaloBlurTransition(options);
- assertEquals(layer.getIconHaloBlurTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconHaloBlurTransition(options);
+ assertEquals(layer.getIconHaloBlurTransition(), options);
+ });
}
@Test
@@ -2712,11 +3512,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconHaloBlur(0.3f));
- assertEquals((Float) layer.getIconHaloBlur().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(iconHaloBlur(0.3f));
+ assertEquals((Float) layer.getIconHaloBlur().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -2724,26 +3526,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloBlur(
- zoom(
- exponential(
- stop(2, iconHaloBlur(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2751,19 +3555,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- iconHaloBlur(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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
@@ -2771,26 +3577,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloBlur(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconHaloBlur(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2798,29 +3606,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ iconHaloBlur(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, iconHaloBlur(0.3f))
+ )
+ ).withDefaultValue(iconHaloBlur(0.3f))
+ )
+ );
- // 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());
+ });
- // 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());
}
@Test
@@ -2828,34 +3639,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blur");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -2863,12 +3676,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-translateTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setIconTranslateTransition(options);
- assertEquals(layer.getIconTranslateTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconTranslateTransition(options);
+ assertEquals(layer.getIconTranslateTransition(), options);
+ });
}
@Test
@@ -2876,11 +3691,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-translate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconTranslate(new Float[]{0f,0f}));
- assertEquals((Float[]) layer.getIconTranslate().getValue(), (Float[]) new Float[]{0f,0f});
+ // Set and Get
+ layer.setProperties(iconTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getIconTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
+ });
}
@Test
@@ -2888,26 +3705,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-translate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconTranslate(
- zoom(
- exponential(
- stop(2, iconTranslate(new Float[]{0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2915,11 +3734,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-translate-anchor");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(iconTranslateAnchor(ICON_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getIconTranslateAnchor().getValue(), (String) ICON_TRANSLATE_ANCHOR_MAP);
+ // Set and Get
+ layer.setProperties(iconTranslateAnchor(ICON_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getIconTranslateAnchor().getValue(), (String) ICON_TRANSLATE_ANCHOR_MAP);
+ });
}
@Test
@@ -2927,25 +3748,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-translate-anchor");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconTranslateAnchor(
- zoom(
- interval(
- stop(2, iconTranslateAnchor(ICON_TRANSLATE_ANCHOR_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -2953,12 +3776,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-opacityTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setTextOpacityTransition(options);
- assertEquals(layer.getTextOpacityTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextOpacityTransition(options);
+ assertEquals(layer.getTextOpacityTransition(), options);
+ });
}
@Test
@@ -2966,11 +3791,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textOpacity(0.3f));
- assertEquals((Float) layer.getTextOpacity().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textOpacity(0.3f));
+ assertEquals((Float) layer.getTextOpacity().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -2978,26 +3805,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOpacity(
- zoom(
- exponential(
- stop(2, textOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3005,19 +3834,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -3025,26 +3856,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-opacity");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textOpacity(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3052,29 +3885,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-opacity");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textOpacity(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textOpacity(0.3f))
+ )
+ ).withDefaultValue(textOpacity(0.3f))
+ )
+ );
- // 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());
+ });
- // 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());
}
@Test
@@ -3082,34 +3918,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-opacity");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -3117,12 +3955,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-colorTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setTextColorTransition(options);
- assertEquals(layer.getTextColorTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextColorTransition(options);
+ assertEquals(layer.getTextColorTransition(), options);
+ });
}
@Test
@@ -3130,11 +3970,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(textColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getTextColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -3142,26 +3984,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textColor(
- zoom(
- exponential(
- stop(2, textColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3169,19 +4013,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -3189,26 +4035,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, textColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3216,29 +4064,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textColor(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop("valueA", textColor(Color.RED))
+ )
+ ).withDefaultValue(textColor(Color.GREEN))
+ )
+ );
- // 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());
+ });
- // 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
@@ -3246,11 +4097,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textColor(Color.RED));
- assertEquals(layer.getTextColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(textColor(Color.RED));
+ assertEquals(layer.getTextColorAsInt(), Color.RED);
+ });
}
@Test
@@ -3258,12 +4111,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-colorTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setTextHaloColorTransition(options);
- assertEquals(layer.getTextHaloColorTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextHaloColorTransition(options);
+ assertEquals(layer.getTextHaloColorTransition(), options);
+ });
}
@Test
@@ -3271,11 +4126,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, 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)");
+ // Set and Get
+ layer.setProperties(textHaloColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getTextHaloColor().getValue(), (String) "rgba(0, 0, 0, 1)");
+ });
}
@Test
@@ -3283,26 +4140,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloColor(
- zoom(
- exponential(
- stop(2, textHaloColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3310,19 +4169,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textHaloColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -3330,26 +4191,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-color");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, textHaloColor(Color.RED))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3357,29 +4220,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textHaloColor(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop("valueA", textHaloColor(Color.RED))
+ )
+ ).withDefaultValue(textHaloColor(Color.GREEN))
+ )
+ );
- // 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());
+ });
- // 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
@@ -3387,11 +4253,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-color");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textHaloColor(Color.RED));
- assertEquals(layer.getTextHaloColorAsInt(), Color.RED);
+ // Set and Get
+ layer.setProperties(textHaloColor(Color.RED));
+ assertEquals(layer.getTextHaloColorAsInt(), Color.RED);
+ });
}
@Test
@@ -3399,12 +4267,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-widthTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setTextHaloWidthTransition(options);
- assertEquals(layer.getTextHaloWidthTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextHaloWidthTransition(options);
+ assertEquals(layer.getTextHaloWidthTransition(), options);
+ });
}
@Test
@@ -3412,11 +4282,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textHaloWidth(0.3f));
- assertEquals((Float) layer.getTextHaloWidth().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textHaloWidth(0.3f));
+ assertEquals((Float) layer.getTextHaloWidth().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -3424,26 +4296,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloWidth(
- zoom(
- exponential(
- stop(2, textHaloWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3451,19 +4325,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textHaloWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -3471,26 +4347,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-width");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textHaloWidth(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3498,29 +4376,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-width");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textHaloWidth(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textHaloWidth(0.3f))
+ )
+ ).withDefaultValue(textHaloWidth(0.3f))
+ )
+ );
- // 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());
+ });
- // 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());
}
@Test
@@ -3528,34 +4409,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-width");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -3563,12 +4446,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blurTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setTextHaloBlurTransition(options);
- assertEquals(layer.getTextHaloBlurTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextHaloBlurTransition(options);
+ assertEquals(layer.getTextHaloBlurTransition(), options);
+ });
}
@Test
@@ -3576,11 +4461,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textHaloBlur(0.3f));
- assertEquals((Float) layer.getTextHaloBlur().getValue(), (Float) 0.3f);
+ // Set and Get
+ layer.setProperties(textHaloBlur(0.3f));
+ assertEquals((Float) layer.getTextHaloBlur().getValue(), (Float) 0.3f);
+ });
}
@Test
@@ -3588,26 +4475,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloBlur(
- zoom(
- exponential(
- stop(2, textHaloBlur(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3615,19 +4504,21 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- textHaloBlur(property("FeaturePropertyA", Stops.<Float>identity()))
- );
+ // 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());
+ // 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());
+ });
}
@Test
@@ -3635,26 +4526,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blur");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloBlur(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textHaloBlur(0.3f))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3662,29 +4555,32 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blur");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textHaloBlur(
+ property(
+ "FeaturePropertyA",
+ categorical(
+ stop(1.0f, textHaloBlur(0.3f))
+ )
+ ).withDefaultValue(textHaloBlur(0.3f))
+ )
+ );
- // 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());
+ });
- // 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());
}
@Test
@@ -3692,34 +4588,36 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blur");
- 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);
+ invoke(mapboxMap, (uiController, 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
@@ -3727,12 +4625,14 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-translateTransitionOptions");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setTextTranslateTransition(options);
- assertEquals(layer.getTextTranslateTransition(), options);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextTranslateTransition(options);
+ assertEquals(layer.getTextTranslateTransition(), options);
+ });
}
@Test
@@ -3740,11 +4640,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-translate");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textTranslate(new Float[]{0f,0f}));
- assertEquals((Float[]) layer.getTextTranslate().getValue(), (Float[]) new Float[]{0f,0f});
+ // Set and Get
+ layer.setProperties(textTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getTextTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
+ });
}
@Test
@@ -3752,26 +4654,28 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-translate");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTranslate(
- zoom(
- exponential(
- stop(2, textTranslate(new Float[]{0f,0f}))
- ).withBase(0.5f)
+ invoke(mapboxMap, (uiController, 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());
+ // 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());
+ });
}
@Test
@@ -3779,11 +4683,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-translate-anchor");
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(textTranslateAnchor(TEXT_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getTextTranslateAnchor().getValue(), (String) TEXT_TRANSLATE_ANCHOR_MAP);
+ // Set and Get
+ layer.setProperties(textTranslateAnchor(TEXT_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getTextTranslateAnchor().getValue(), (String) TEXT_TRANSLATE_ANCHOR_MAP);
+ });
}
@Test
@@ -3791,25 +4697,27 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-translate-anchor");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTranslateAnchor(
- zoom(
- interval(
- stop(2, textTranslateAnchor(TEXT_TRANSLATE_ANCHOR_MAP))
+ invoke(mapboxMap, (uiController, 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());
+ );
+
+ // 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());
+ });
}
}
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 02aedadfa5..192740f708 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,9 +7,9 @@
package com.mapbox.mapboxsdk.testapp.style;
import android.graphics.Color;
-import android.support.test.espresso.Espresso;
-import android.support.test.rule.ActivityTestRule;
+import android.support.test.espresso.UiController;
import android.support.test.runner.AndroidJUnit4;
+
import timber.log.Timber;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@@ -23,20 +23,16 @@ 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.<%- camelize(type) %>Layer;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource;
+import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction;
import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static com.mapbox.mapboxsdk.style.functions.Function.*;
import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
import static com.mapbox.mapboxsdk.style.functions.stops.Stops.*;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
@@ -57,19 +53,30 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
return EspressoTestActivity.class;
}
- private void setupLayer(){
+ private void setupLayer() {
<% if (type === 'background') { -%>
Timber.i("Retrieving layer");
- layer = mapboxMap.getLayerAs("background");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ layer = mapboxMap.getLayerAs("background");
+ }
+ });
<% } else { -%>
- 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");
- }
+ 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");
+ }
+ }
+ });
<% } -%>
}
@@ -78,14 +85,19 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- assertNotNull(layer);
-
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
-
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
+
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
+ }
+ });
}
<% if (!(type === 'background' || type === 'raster')) { -%>
@@ -94,15 +106,20 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("SourceLayer");
- assertNotNull(layer);
-
- // Get initial
- assertEquals(layer.getSourceLayer(), "composite");
-
- // Set
- final String sourceLayer = "test";
- layer.setSourceLayer(sourceLayer);
- assertEquals(layer.getSourceLayer(), 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);
+ }
+ });
}
<% } -%>
<% for (const property of properties) { -%>
@@ -113,12 +130,17 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>TransitionOptions");
- 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);
+ 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.set<%- camelize(property.name) %>Transition(options);
+ assertEquals(layer.get<%- camelize(property.name) %>Transition(), options);
+ }
+ });
}
<% } -%>
@@ -127,11 +149,16 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- 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) %>);
+ 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) %>);
+ }
+ });
}
<% if (supportsZoomFunction(property)) { -%>
@@ -140,37 +167,42 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- zoom(
+ 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) %>))
- )
+ interval(
+ stop(2, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
+ )
<% } else { -%>
- exponential(
- stop(2, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
- ).withBase(0.5f)
+ 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());
+ )
+ )
+ );
+
+ // 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());
+ 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());
+ 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)) { -%>
@@ -180,19 +212,24 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- 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());
+ 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') { -%>
@@ -201,30 +238,35 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- property(
- "FeaturePropertyA",
- interval(
+ 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))
+ stop(Color.RED, <%- camelizeWithLeadingLowercase(property.name) %>(Color.RED))
<% } else {-%>
- stop(1, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
- )
+ 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());
+ )
+ )
+ );
+
+ // 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 if (property.type === 'array') { -%>
@@ -233,30 +275,35 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- property(
- "FeaturePropertyA",
- interval(
+ 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))
+ stop(Color.RED, <%- camelizeWithLeadingLowercase(property.name) %>(Color.RED))
<% } else {-%>
- stop(1, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
+ 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());
+ );
+
+ // 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 { -%>
@@ -265,30 +312,35 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- property(
- "FeaturePropertyA",
- exponential(
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ <%- camelizeWithLeadingLowercase(property.name) %>(
+ property(
+ "FeaturePropertyA",
+ exponential(
<% if (property.type == 'color') { -%>
- stop(Color.RED, <%- camelizeWithLeadingLowercase(property.name) %>(Color.RED))
+ stop(Color.RED, <%- camelizeWithLeadingLowercase(property.name) %>(Color.RED))
<% } else {-%>
- stop(<%- defaultValueJava(property) %>, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
+ 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());
+ ).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());
+ }
+ });
}
@Test
@@ -296,39 +348,45 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- property(
- "FeaturePropertyA",
- categorical(
+ 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))
+ 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) %>))
+ 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());
+ )
+ );
+
+ // 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());
+ 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());
+ assertEquals(<%- defaultValueJava(property) %>, ((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getDefaultValue().getValue());
<% } -%>
+ }
+ });
+
}
<% if (property.type !== 'color') { -%>
@@ -337,38 +395,43 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, <%- camelizeWithLeadingLowercase(property.name) %>(0.9f))
- ).withBase(0.5f)
+ 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) %>))
+ ).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);
+ )
+ );
+
+ // 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);
+ }
+ });
}
<% } -%>
<% } -%>
@@ -380,11 +443,16 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(Color.RED));
- assertEquals(layer.get<%- camelize(property.name) %>AsInt(), Color.RED);
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(Color.RED));
+ assertEquals(layer.get<%- camelize(property.name) %>AsInt(), Color.RED);
+ }
+ });
}
<% } -%>
<% } -%>
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 cb3ba58100..2f22a8f3f0 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,6 +9,7 @@ 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;
@@ -16,6 +17,7 @@ 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;
@@ -35,6 +37,7 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionCol
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -48,14 +51,18 @@ public class LightTest extends BaseActivityTest {
@Test
public void test<%- camelize(property.name) %>Transition() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("<%- property.name %>TransitionOptions");
- 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, 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());
+ }
+ });
}
<% } -%>
<% if (property.name == "position") { -%>
@@ -63,35 +70,44 @@ public class LightTest extends BaseActivityTest {
@Test
public void test<%- camelize(property.name) %>() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("<%- property.name %>");
- 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, 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) %>());
+ }
+ });
}
<% } else { -%>
@Test
public void test<%- camelize(property.name) %>() {
validateTestSetup();
- setupLayer();
+ setupLight();
Timber.i("<%- property.name %>");
- assertNotNull(light);
- // Set and Get
- light.set<%- camelize(property.name) %>(<%- defaultValueJava(property) %>);
+ 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) %>);
<% 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) %>());
<% } -%>
+ }
+ });
}
<% } -%>
<% } -%>
- private void setupLayer() {
+ private void setupLight() {
onView(withId(R.id.mapView)).perform(new ViewAction() {
@Override
public Matcher<View> getConstraints() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java
index 6e582c6a3a..0e2e4587ee 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java
@@ -3,20 +3,25 @@ package com.mapbox.mapboxsdk.testapp.utils;
import android.app.Activity;
import android.support.test.espresso.IdlingResource;
-import timber.log.Timber;
-
+import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import java.lang.reflect.Field;
-public class OnMapReadyIdlingResource implements IdlingResource {
+public class OnMapReadyIdlingResource implements IdlingResource, OnMapReadyCallback {
- private final Activity activity;
private MapboxMap mapboxMap;
private IdlingResource.ResourceCallback resourceCallback;
public OnMapReadyIdlingResource(Activity activity) {
- this.activity = activity;
+ try {
+ Field field = activity.getClass().getDeclaredField("mapView");
+ field.setAccessible(true);
+ ((MapView) field.get(activity)).getMapAsync(this);
+ } catch (Exception err) {
+ throw new RuntimeException(err);
+ }
}
@Override
@@ -26,11 +31,7 @@ public class OnMapReadyIdlingResource implements IdlingResource {
@Override
public boolean isIdleNow() {
- boolean idle = isMapboxMapReady();
- if (idle && resourceCallback != null) {
- resourceCallback.onTransitionToIdle();
- }
- return idle;
+ return mapboxMap != null;
}
@Override
@@ -38,20 +39,15 @@ public class OnMapReadyIdlingResource implements IdlingResource {
this.resourceCallback = resourceCallback;
}
- private boolean isMapboxMapReady() {
- try {
- Field field = activity.getClass().getDeclaredField("mapboxMap");
- field.setAccessible(true);
- mapboxMap = (MapboxMap) field.get(activity);
- Timber.e("isMapboxReady called with value " + (mapboxMap != null));
- return mapboxMap != null;
- } catch (Exception exception) {
- Timber.e("could not reflect", exception);
- return false;
- }
- }
-
public MapboxMap getMapboxMap() {
return mapboxMap;
}
-}
+
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ if (resourceCallback != null) {
+ resourceCallback.onTransitionToIdle();
+ }
+ }
+} \ 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 d57136755f..2533c5de35 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -113,6 +113,17 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
+ android:name=".activity.camera.CameraAnimatorActivity"
+ android:description="@string/description_camera_animator"
+ android:label="@string/activity_camera_animator">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
android:name=".activity.camera.CameraPositionActivity"
android:description="@string/description_cameraposition"
android:label="@string/activity_camera_position">
@@ -305,14 +316,12 @@
</activity>
<activity
android:name=".activity.maplayout.DebugModeActivity"
+ android:configChanges="orientation|keyboardHidden|screenSize"
android:description="@string/description_debug_mode"
android:label="@string/activity_debug_mode">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value="@string/category_basic"/>
</activity>
<activity
android:name=".activity.offline.OfflineActivity"
@@ -359,6 +368,39 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
+ android:name=".activity.snapshot.MapSnapshotterActivity"
+ android:description="@string/description_map_snapshotter"
+ android:label="@string/activity_map_snapshotter">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_imagegenerator"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
+ android:name=".activity.snapshot.MapSnapshotterReuseActivity"
+ android:description="@string/description_map_snapshotter_reuse"
+ android:label="@string/activity_map_snapshotter_reuse">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_imagegenerator"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
+ android:name=".activity.snapshot.MapSnapshotterMarkerActivity"
+ android:description="@string/description_map_snapshotter_marker"
+ android:label="@string/activity_map_snapshotter_marker">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_imagegenerator"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
android:name=".activity.maplayout.DoubleMapActivity"
android:description="@string/description_doublemap"
android:label="@string/activity_double_map">
@@ -392,10 +434,20 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
- android:name=".activity.maplayout.NavigationDrawerActivity"
- android:description="@string/description_navigation_drawer"
- android:label="@string/activity_navigation_drawer"
- android:theme="@style/AppTheme.ActionBar.Transparent">
+ android:name=".activity.maplayout.SimpleMapActivity"
+ android:description="@string/description_simple_map"
+ android:label="@string/activity_simple_map">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_basic"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
+ <activity
+ android:name=".activity.maplayout.MapChangeActivity"
+ android:description="@string/description_map_change"
+ android:label="@string/activity_map_change">
<meta-data
android:name="@string/category"
android:value="@string/category_maplayout"/>
@@ -404,12 +456,12 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
- android:name=".activity.maplayout.SimpleMapActivity"
- android:description="@string/description_simple_map"
- android:label="@string/activity_simple_map">
+ android:name=".activity.maplayout.VisibilityChangeActivity"
+ android:description="@string/description_visibility_map"
+ android:label="@string/activity_map_visibility">
<meta-data
android:name="@string/category"
- android:value="@string/category_basic"/>
+ android:value="@string/category_maplayout"/>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
@@ -537,8 +589,19 @@
</activity>
<activity
android:name=".activity.style.AnimatedImageSourceActivity"
- android:label="@string/activity_animated_image_source"
- android:description="@string/description_animated_image_source">
+ android:description="@string/description_animated_image_source"
+ android:label="@string/activity_animated_image_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>
+ <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"/>
@@ -546,7 +609,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
-
<!-- Features -->
<activity
android:name=".activity.feature.QueryRenderedFeaturesPropertiesActivity"
@@ -604,12 +666,23 @@
android:value=".activity.FeatureOverviewActivity"/>
</activity>
<activity
- android:name=".activity.annotation.AddRemoveMarkerActivity"
+ android:name=".activity.style.SymbolGeneratorActivity"
+ android:description="@string/description_symbol_generator"
+ android:label="@string/activity_symbol_generator">
+ <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.ZoomFunctionSymbolLayerActivity"
android:description="@string/description_add_remove_markers"
android:label="@string/activity_add_remove_markers">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_style"/>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
@@ -660,7 +733,51 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity"/>
</activity>
+ <activity
+ android:name=".activity.maplayout.BottomSheetActivity"
+ android:description="@string/description_bottom_sheet"
+ android:label="@string/activity_bottom_sheet">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_maplayout"/>
+ </activity>
+ <!-- TextureView -->
+ <activity
+ android:name=".activity.textureview.TextureViewDebugModeActivity"
+ android:description="@string/description_textureview_debug"
+ android:label="@string/activity_textureview_debug">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_textureview"/>
+ </activity>
+ <activity
+ android:name=".activity.textureview.TextureViewResizeActivity"
+ android:description="@string/description_textureview_resize"
+ android:label="@string/activity_textureview_resize">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_textureview"/>
+ </activity>
+ <activity
+ android:name=".activity.textureview.TextureViewAnimationActivity"
+ android:description="@string/description_textureview_animate"
+ android:label="@string/activity_textureview_animate">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_textureview"/>
+ </activity>
+ <activity
+ android:name=".activity.maplayout.LocalGlyphActivity"
+ android:description="@string/description_local_glyph"
+ android:label="@string/activity_local_glyph">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_maplayout"/>
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity"/>
+ </activity>
<!-- For Instrumentation tests -->
<activity
@@ -689,4 +806,4 @@
<!-- android:value="true" /> -->
</application>
-</manifest>
+</manifest> \ No newline at end of file
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 deee312bb3..fba33bb380 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
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp;
import android.app.Application;
import android.os.StrictMode;
import android.text.TextUtils;
-import android.util.Log;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.testapp.utils.TokenUtils;
@@ -21,7 +20,6 @@ import static timber.log.Timber.DebugTree;
*/
public class MapboxApplication extends Application {
- private static final String LOG_TAG = MapboxApplication.class.getSimpleName();
private static final String DEFAULT_MAPBOX_ACCESS_TOKEN = "YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE";
private static final String ACCESS_TOKEN_NOT_SET_MESSAGE = "In order to run the Test App you need to set a valid "
+ "access token. During development, you can set the MAPBOX_ACCESS_TOKEN environment variable for the SDK to "
@@ -55,7 +53,7 @@ public class MapboxApplication extends Application {
String mapboxAccessToken = TokenUtils.getMapboxAccessToken(getApplicationContext());
if (TextUtils.isEmpty(mapboxAccessToken) || mapboxAccessToken.equals(DEFAULT_MAPBOX_ACCESS_TOKEN)) {
- Log.w(LOG_TAG, ACCESS_TOKEN_NOT_SET_MESSAGE);
+ Timber.e(ACCESS_TOKEN_NOT_SET_MESSAGE);
}
Mapbox.getInstance(getApplicationContext(), mapboxAccessToken);
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 074be98f5c..95cc9687f2 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
@@ -1,6 +1,5 @@
package com.mapbox.mapboxsdk.testapp.activity;
-import android.Manifest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -13,18 +12,18 @@ import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
-import android.support.v4.app.ActivityCompat;
-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.View;
+import android.text.TextUtils;
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,37 +39,38 @@ import timber.log.Timber;
* It uses tags as category and description to order the different entries.
* </p>
*/
-public class FeatureOverviewActivity extends AppCompatActivity {
+public class FeatureOverviewActivity extends AppCompatActivity implements PermissionsListener {
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;
- }
+ ItemClickSupport.addTo(recyclerView).setOnItemClickListener((recyclerView, position, view) -> {
+ if (!sectionAdapter.isSectionHeaderPosition(position)) {
+ int itemPosition = sectionAdapter.getConvertedPosition(position);
+ Feature feature = features.get(itemPosition);
+ if (feature.isRequiresLocationPermission()) {
+ if (requestLocationPermission(itemPosition)) {
+ return;
}
- startFeature(feature);
}
+ startFeature(feature);
}
});
@@ -88,7 +88,7 @@ public class FeatureOverviewActivity extends AppCompatActivity {
getPackageManager().getPackageInfo(getPackageName(),
PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA));
} catch (PackageManager.NameNotFoundException exception) {
- Timber.e("Could not resolve package info", exception);
+ Timber.e(exception, "Could not resolve package info");
}
}
@@ -119,22 +119,26 @@ public class FeatureOverviewActivity extends AppCompatActivity {
}
private boolean requestLocationPermission(final int positionInList) {
- if ((ContextCompat.checkSelfPermission(FeatureOverviewActivity.this,
- Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
- || (ContextCompat.checkSelfPermission(FeatureOverviewActivity.this,
- Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
- ActivityCompat.requestPermissions(FeatureOverviewActivity.this, new String[] {
- Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, positionInList);
+ if (isRuntimePermissionsRequired()) {
+ locationActivityInList = positionInList;
+ permissionsManager.requestLocationPermissions(this);
return true;
- } else {
- return false;
}
+ return false;
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- if (!isRuntimePermissionsRequired() || isPermissionAccepted(grantResults)) {
- startFeature(features.get(requestCode));
+ 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),
@@ -143,12 +147,14 @@ public class FeatureOverviewActivity extends AppCompatActivity {
}
}
- private boolean isRuntimePermissionsRequired() {
- return android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- private boolean isPermissionAccepted(@NonNull int[] grantResults) {
- return grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ private boolean isRuntimePermissionsRequired() {
+ return android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
@Override
@@ -178,15 +184,12 @@ public class FeatureOverviewActivity extends AppCompatActivity {
}
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);
}
@@ -219,13 +222,13 @@ public class FeatureOverviewActivity extends AppCompatActivity {
}
};
- List<String> requiresPermissionActvities = new ArrayList<String>() {
+ List<String> requiresPermissionActivities = new ArrayList<String>() {
{
add(resources.getString(R.string.activity_double_map));
}
};
- return requiresPermissionCategories.contains(category) || requiresPermissionActvities.contains(name);
+ return requiresPermissionCategories.contains(category) || requiresPermissionActivities.contains(name);
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java
deleted file mode 100644
index 27958c3d0c..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java
+++ /dev/null
@@ -1,188 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.annotation;
-
-import android.annotation.SuppressLint;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.support.annotation.DrawableRes;
-import android.support.v7.app.AppCompatActivity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.mapbox.mapboxsdk.annotations.Icon;
-import com.mapbox.mapboxsdk.annotations.IconFactory;
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-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.testapp.R;
-import com.mapbox.mapboxsdk.testapp.utils.ViewToBitmapUtil;
-
-import timber.log.Timber;
-
-/**
- * Test activity showcasing updating a Marker image when changing zoom levels
- */
-public class AddRemoveMarkerActivity extends AppCompatActivity {
-
- public static final double THRESHOLD = 5.0;
-
- private MapView mapView;
- private MapboxMap mapboxMap;
- private double lastZoom;
- private boolean isShowingHighThresholdMarker;
- private boolean isShowingLowThresholdMarker;
-
- private MarkerOptions lowThresholdMarker;
- private MarkerOptions highThresholdMarker;
- private Marker activeMarker;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_add_remove_marker);
-
- View lowThresholdView = generateView("Low", R.drawable.ic_circle);
- Bitmap lowThresholdBitmap = ViewToBitmapUtil.convertToBitmap(lowThresholdView);
- Icon lowThresholdIcon = IconFactory.getInstance(this).fromBitmap(lowThresholdBitmap);
-
- lowThresholdMarker = new MarkerOptions()
- .icon(lowThresholdIcon)
- .position(new LatLng(0.1, 0));
-
- View highThesholdView = generateView("High", R.drawable.ic_circle);
- Bitmap highThresholdBitmap = ViewToBitmapUtil.convertToBitmap(highThesholdView);
- Icon highThresholdIcon = IconFactory.getInstance(this).fromBitmap(highThresholdBitmap);
-
- highThresholdMarker = new MarkerOptions()
- .icon(highThresholdIcon)
- .position(new LatLng(0.1, 0));
-
- mapView = (MapView) findViewById(R.id.mapView);
- mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- AddRemoveMarkerActivity.this.mapboxMap = mapboxMap;
- updateZoom(mapboxMap.getCameraPosition().zoom);
- mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4.9f));
- mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
- @Override
- public void onCameraChange(CameraPosition position) {
- updateZoom(position.zoom);
- }
- });
- }
- });
- }
-
- @SuppressLint("InflateParams")
- private View generateView(String text, @DrawableRes int drawableRes) {
- View view = LayoutInflater.from(this).inflate(R.layout.view_custom_marker, null);
- TextView textView = (TextView) view.findViewById(R.id.textView);
- textView.setText(text);
- textView.setTextColor(Color.WHITE);
- ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
- imageView.setImageResource(drawableRes);
- return view;
- }
-
- private void updateZoom(double zoom) {
- if (lastZoom == zoom) {
- return;
- }
-
- lastZoom = zoom;
- if (zoom > THRESHOLD) {
- showHighThresholdMarker();
- } else {
- showLowThresholdMarker();
- }
- }
-
- private void showLowThresholdMarker() {
- if (isShowingLowThresholdMarker) {
- return;
- }
-
- isShowingLowThresholdMarker = true;
- isShowingHighThresholdMarker = false;
-
- if (activeMarker != null) {
- Timber.d("Remove marker with " + activeMarker.getId());
- mapboxMap.removeMarker(activeMarker);
- } else {
- Timber.e("active marker is null");
- }
-
- activeMarker = mapboxMap.addMarker(lowThresholdMarker);
- Timber.d("showLowThresholdMarker() " + activeMarker.getId());
- }
-
- private void showHighThresholdMarker() {
- if (isShowingHighThresholdMarker) {
- return;
- }
-
- isShowingLowThresholdMarker = false;
- isShowingHighThresholdMarker = true;
-
- if (activeMarker != null) {
- Timber.d("Remove marker with " + activeMarker.getId());
- mapboxMap.removeMarker(activeMarker);
- } else {
- Timber.e("active marker is null");
- }
-
- activeMarker = mapboxMap.addMarker(highThresholdMarker);
- Timber.d("showHighThresholdMarker() " + activeMarker.getId());
- }
-
- @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
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
index 52181cee0c..a557bb4ed4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
@@ -7,7 +7,6 @@ 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;
@@ -24,7 +23,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.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
import com.mapbox.services.api.utils.turf.TurfMeasurement;
@@ -59,25 +57,18 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
+ mapView.getMapAsync(mapboxMap -> {
+ AnimatedMarkerActivity.this.mapboxMap = mapboxMap;
+ setupMap();
- @Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- AnimatedMarkerActivity.this.mapboxMap = mapboxMap;
- setupMap();
-
- animationRunnable = new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < 10; i++) {
- addRandomCar();
- }
- addPassenger();
- addMainCar();
- }
- };
- mapView.post(animationRunnable);
- }
+ animationRunnable = () -> {
+ for (int i = 0; i < 10; i++) {
+ addRandomCar();
+ }
+ addPassenger();
+ addMainCar();
+ };
+ mapView.post(animationRunnable);
});
}
@@ -116,13 +107,10 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
if (carMarker == null) {
carMarker = createCarMarker(randomLatLng, R.drawable.ic_taxi_top,
- new MarkerViewManager.OnMarkerViewAddedListener() {
- @Override
- public void onViewAdded(@NonNull MarkerView markerView) {
- // Make sure the car marker is selected so that it's always brought to the front (#5285)
- mapboxMap.selectMarker(carMarker);
- animateMoveToPassenger(carMarker);
- }
+ markerView -> {
+ // Make sure the car marker is selected so that it's always brought to the front (#5285)
+ mapboxMap.selectMarker(carMarker);
+ animateMoveToPassenger(carMarker);
});
markerViews.add(carMarker);
} else {
@@ -147,12 +135,7 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
protected void addRandomCar() {
markerViews.add(createCarMarker(getLatLngInBounds(), R.drawable.ic_car_top,
- new MarkerViewManager.OnMarkerViewAddedListener() {
- @Override
- public void onViewAdded(@NonNull MarkerView markerView) {
- randomlyMoveMarker(markerView);
- }
- }));
+ markerView -> randomlyMoveMarker(markerView)));
}
private void randomlyMoveMarker(final MarkerView marker) {
@@ -236,12 +219,14 @@ public class AnimatedMarkerActivity extends AppCompatActivity {
stopped = true;
- // Stop ongoing animations, prevent memory lekas
- MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager();
- for (MarkerView markerView : markerViews) {
- View view = markerViewManager.getView(markerView);
- if (view != null) {
- view.animate().cancel();
+ // 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();
+ }
}
}
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 8b238e49a8..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,11 +86,16 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView
}
private void onLatLngListLoaded(List<LatLng> latLngs, int amount) {
+ progressDialog.hide();
locations = latLngs;
showMarkers(amount);
}
private void showMarkers(int amount) {
+ if (mapboxMap == null || locations == null) {
+ return;
+ }
+
mapboxMap.clear();
if (locations.size() < amount) {
@@ -228,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;
});
}
}
@@ -258,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("Could not add markers,", exception);
- 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 f2f82865d1..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,13 +29,13 @@ 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;
import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerView;
import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerViewOptions;
+import java.util.Locale;
import java.util.Random;
/**
@@ -80,123 +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("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/MarkerViewsInRectangleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java
index 266db3fdd1..848eab9a3c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java
@@ -19,6 +19,9 @@ import java.util.List;
import timber.log.Timber;
+/**
+ * Test activity showcasing counting MarkerViews in a rectangle.
+ */
public class MarkerViewsInRectangleActivity extends AppCompatActivity implements OnMapReadyCallback,
View.OnClickListener {
@@ -53,7 +56,7 @@ public class MarkerViewsInRectangleActivity extends AppCompatActivity implements
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(String.format("Querying box %s", box));
+ Timber.i("Querying box %s", box);
List<MarkerView> markers = mapboxMap.getMarkerViewsInRect(box);
// Show count
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 b51d717f33..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
@@ -5,6 +5,7 @@ import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
+import android.widget.Toast;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
@@ -55,8 +56,6 @@ public class PolygonActivity extends AppCompatActivity implements OnMapReadyCall
// configure inital map state
MapboxMapOptions options = new MapboxMapOptions()
.attributionTintColor(RED_COLOR)
- // deprecated feature!
- .textureMode(true)
.compassFadesWhenFacingNorth(false)
.styleUrl(Style.MAPBOX_STREETS)
.camera(new CameraPosition.Builder()
@@ -77,6 +76,13 @@ public class PolygonActivity extends AppCompatActivity implements OnMapReadyCall
@Override
public void onMapReady(MapboxMap map) {
mapboxMap = map;
+
+ 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)
.fillColor(BLUE_COLOR));
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 0aaa6127f4..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,34 +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 MapboxMap mapboxMap) {
- PolylineActivity.this.mapboxMap = mapboxMap;
- polylines = mapboxMap.addPolylines(polylineOptions);
- }
+ mapView.getMapAsync(mapboxMap -> {
+ PolylineActivity.this.mapboxMap = mapboxMap;
+
+ 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
new file mode 100644
index 0000000000..f25fd6ab27
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java
@@ -0,0 +1,265 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
+import android.os.Bundle;
+import android.support.v4.util.LongSparseArray;
+import android.support.v4.view.animation.FastOutLinearInInterpolator;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.support.v4.view.animation.PathInterpolatorCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.animation.AnticipateOvershootInterpolator;
+import android.view.animation.BounceInterpolator;
+import android.view.animation.Interpolator;
+
+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.testapp.R;
+
+/**
+ * Test activity showcasing using Android SDK animators to animate camera position changes.
+ */
+public class CameraAnimatorActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private static final double ANIMATION_DELAY_FACTOR = 1.5;
+ private static final LatLng START_LAT_LNG = new LatLng(37.787947, -122.407432);
+
+ private final LongSparseArray<AnimatorBuilder> animators = new LongSparseArray<AnimatorBuilder>() {
+ {
+ 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, () -> {
+ 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, () ->
+ obtainExampleInterpolator(new AnticipateOvershootInterpolator(), 2500)
+ );
+
+ put(R.id.menu_action_path_interpolator, () -> obtainExampleInterpolator(
+ PathInterpolatorCompat.create(.22f, .68f, 0, 1.71f), 2500));
+ }
+ };
+
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_camera_animator);
+ mapView = (MapView) findViewById(R.id.mapView);
+ if (mapView != null) {
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+ }
+
+ @Override
+ public void onMapReady(final MapboxMap map) {
+ mapboxMap = map;
+ initFab();
+ }
+
+ private void initFab() {
+ 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();
+ });
+ }
+
+ //
+ // Animator API used for the animation on the FAB
+ //
+
+ private Animator createExampleAnimator(CameraPosition currentPosition, CameraPosition targetPosition) {
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.play(createLatLngAnimator(currentPosition.target, targetPosition.target));
+ animatorSet.play(createZoomAnimator(currentPosition.zoom, targetPosition.zoom));
+ animatorSet.play(createBearingAnimator(currentPosition.bearing, targetPosition.bearing));
+ animatorSet.play(createTiltAnimator(currentPosition.tilt, targetPosition.tilt));
+ return animatorSet;
+ }
+
+ private Animator createLatLngAnimator(LatLng currentPosition, LatLng targetPosition) {
+ ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, targetPosition);
+ latLngAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ latLngAnimator.setInterpolator(new FastOutSlowInInterpolator());
+ latLngAnimator.addUpdateListener(animation -> mapboxMap.setLatLng((LatLng) animation.getAnimatedValue()));
+ return latLngAnimator;
+ }
+
+ private Animator createZoomAnimator(double currentZoom, double targetZoom) {
+ ValueAnimator zoomAnimator = ValueAnimator.ofFloat((float) currentZoom, (float) targetZoom);
+ zoomAnimator.setDuration((long) (2200 * ANIMATION_DELAY_FACTOR));
+ zoomAnimator.setStartDelay((long) (600 * ANIMATION_DELAY_FACTOR));
+ zoomAnimator.setInterpolator(new AnticipateOvershootInterpolator());
+ zoomAnimator.addUpdateListener(animation -> mapboxMap.setZoom((Float) animation.getAnimatedValue()));
+ return zoomAnimator;
+ }
+
+ private Animator createBearingAnimator(double currentBearing, double targetBearing) {
+ ValueAnimator bearingAnimator = ValueAnimator.ofFloat((float) currentBearing, (float) targetBearing);
+ bearingAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ bearingAnimator.setStartDelay((long) (1000 * ANIMATION_DELAY_FACTOR));
+ bearingAnimator.setInterpolator(new FastOutLinearInInterpolator());
+ bearingAnimator.addUpdateListener(animation -> mapboxMap.setBearing((Float) animation.getAnimatedValue()));
+ return bearingAnimator;
+ }
+
+ private Animator createTiltAnimator(double currentTilt, double targetTilt) {
+ ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, (float) targetTilt);
+ tiltAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR));
+ tiltAnimator.setStartDelay((long) (1500 * ANIMATION_DELAY_FACTOR));
+ tiltAnimator.addUpdateListener(animation -> mapboxMap.setTilt((Float) animation.getAnimatedValue()));
+ return tiltAnimator;
+ }
+
+ //
+ // Interpolator examples
+ //
+
+ private Animator obtainExampleInterpolator(int menuItemId) {
+ return animators.get(menuItemId).build();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_animator, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (mapboxMap == null) {
+ return false;
+ }
+ findViewById(R.id.fab).setVisibility(View.GONE);
+ resetCameraPosition();
+ playAnimation(item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void resetCameraPosition() {
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(START_LAT_LNG)
+ .zoom(11)
+ .bearing(0)
+ .tilt(0)
+ .build()
+ ));
+ }
+
+ private void playAnimation(int itemId) {
+ Animator animator = obtainExampleInterpolator(itemId);
+ if (animator != null) {
+ animator.start();
+ }
+ }
+
+ private Animator obtainExampleInterpolator(Interpolator interpolator, long duration) {
+ ValueAnimator zoomAnimator = ValueAnimator.ofFloat(11.0f, 16.0f);
+ zoomAnimator.setDuration((long) (duration * ANIMATION_DELAY_FACTOR));
+ zoomAnimator.setInterpolator(interpolator);
+ zoomAnimator.addUpdateListener(animation -> mapboxMap.setZoom((Float) animation.getAnimatedValue()));
+ return zoomAnimator;
+ }
+
+ //
+ // MapView lifecycle
+ //
+
+ @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();
+ }
+
+ /**
+ * Helper class to evaluate LatLng objects with a ValueAnimator
+ */
+ private static class LatLngEvaluator implements TypeEvaluator<LatLng> {
+
+ private final 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;
+ }
+ }
+
+ interface AnimatorBuilder {
+ Animator build();
+ }
+}
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 60518239c8..42711a4379 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
@@ -24,10 +24,16 @@ import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
-public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback {
+/**
+ * Test activity showcasing how to listen to camera change events.
+ */
+public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback, View.OnClickListener,
+ MapboxMap.OnMapLongClickListener {
private MapView mapView;
private MapboxMap mapboxMap;
+ private FloatingActionButton fab;
+ private boolean logCameraChanges;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -40,94 +46,50 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
}
@Override
- public void onMapReady(@NonNull final MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
-
- mapboxMap.setOnCameraIdleListener(new MapboxMap.OnCameraIdleListener() {
- @Override
- public void onCameraIdle() {
- Timber.e("OnCameraIdle");
- }
- });
-
- mapboxMap.setOnCameraMoveCancelListener(new MapboxMap.OnCameraMoveCanceledListener() {
- @Override
- public void onCameraMoveCanceled() {
- Timber.e("OnCameraMoveCanceled");
- }
- });
-
- mapboxMap.setOnCameraMoveListener(new MapboxMap.OnCameraMoveListener() {
- @Override
- public void onCameraMove() {
- Timber.e("OnCameraMove");
- }
- });
-
- mapboxMap.setOnCameraMoveStartedistener(new MapboxMap.OnCameraMoveStartedListener() {
-
- private final String[] REASONS = {"REASON_API_GESTURE", "REASON_DEVELOPER_ANIMATION", "REASON_API_ANIMATION"};
-
- @Override
- public void onCameraMoveStarted(int reason) {
- // reason ranges from 1 <-> 3
- Timber.e("OnCameraMoveStarted: %s", REASONS[reason - 1]);
- }
- });
+ public void onMapReady(@NonNull final MapboxMap map) {
+ mapboxMap = map;
+ toggleLogCameraChanges();
// add a listener to FAB
- FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
+ fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Context context = view.getContext();
- final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_camera_position, null);
- AlertDialog.Builder builder = new AlertDialog.Builder(
- context, com.mapbox.mapboxsdk.R.style.mapbox_AlertDialogStyle);
- builder.setTitle(R.string.dialog_camera_position);
- builder.setView(onInflateDialogContent(dialogContent));
- builder.setPositiveButton("Animate", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- double latitude = Double.parseDouble(
- ((TextView) dialogContent.findViewById(R.id.value_lat)).getText().toString());
- double longitude = Double.parseDouble(
- ((TextView) dialogContent.findViewById(R.id.value_lon)).getText().toString());
- double zoom = Double.parseDouble(
- ((TextView) dialogContent.findViewById(R.id.value_zoom)).getText().toString());
- double bearing = Double.parseDouble(
- ((TextView) dialogContent.findViewById(R.id.value_bearing)).getText().toString());
- double tilt = Double.parseDouble(
- ((TextView) dialogContent.findViewById(R.id.value_tilt)).getText().toString());
-
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(new LatLng(latitude, longitude))
- .zoom(zoom)
- .bearing(bearing)
- .tilt(tilt)
- .build();
-
- mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000,
- new MapboxMap.CancelableCallback() {
- @Override
- public void onCancel() {
- Timber.v("OnCancel called");
- }
-
- @Override
- public void onFinish() {
- Timber.v("OnFinish called");
- }
- });
- Timber.v(cameraPosition.toString());
- }
- });
- builder.setNegativeButton("Cancel", null);
- builder.setCancelable(false);
- builder.show();
- }
- });
+ fab.setOnClickListener(this);
+
+ // listen to long click events to toggle logging camera changes
+ mapboxMap.setOnMapLongClickListener(this);
+ }
+
+ @Override
+ public void onMapLongClick(@NonNull LatLng point) {
+ toggleLogCameraChanges();
+ }
+
+ @Override
+ public void onClick(View view) {
+ Context context = view.getContext();
+ final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_camera_position, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.dialog_camera_position);
+ builder.setView(onInflateDialogContent(dialogContent));
+ builder.setPositiveButton("Animate", new DialogClickListener(mapboxMap, dialogContent));
+ builder.setNegativeButton("Cancel", null);
+ builder.setCancelable(false);
+ builder.show();
+ }
+
+ private void toggleLogCameraChanges() {
+ logCameraChanges = !logCameraChanges;
+ if (logCameraChanges) {
+ mapboxMap.addOnCameraIdleListener(idleListener);
+ mapboxMap.addOnCameraMoveCancelListener(moveCanceledListener);
+ mapboxMap.addOnCameraMoveListener(moveListener);
+ mapboxMap.addOnCameraMoveStartedListener(moveStartedListener);
+ } else {
+ mapboxMap.removeOnCameraIdleListener(idleListener);
+ mapboxMap.removeOnCameraMoveCancelListener(moveCanceledListener);
+ mapboxMap.removeOnCameraMoveListener(moveListener);
+ mapboxMap.removeOnCameraMoveStartedListener(moveStartedListener);
+ }
}
@Override
@@ -184,6 +146,36 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
seekBar.setProgress(defaultValue);
}
+ private MapboxMap.OnCameraIdleListener idleListener = new MapboxMap.OnCameraIdleListener() {
+ @Override
+ public void onCameraIdle() {
+ Timber.e("OnCameraIdle");
+ fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_green_dark));
+ }
+ };
+
+ private MapboxMap.OnCameraMoveListener moveListener = new MapboxMap.OnCameraMoveListener() {
+ @Override
+ public void onCameraMove() {
+ Timber.e("OnCameraMove");
+ fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_orange_dark));
+ }
+ };
+
+ private MapboxMap.OnCameraMoveCanceledListener moveCanceledListener = () -> Timber.e("OnCameraMoveCanceled");
+
+ private MapboxMap.OnCameraMoveStartedListener moveStartedListener = new MapboxMap.OnCameraMoveStartedListener() {
+
+ private final String[] REASONS = {"REASON_API_GESTURE", "REASON_DEVELOPER_ANIMATION", "REASON_API_ANIMATION"};
+
+ @Override
+ public void onCameraMoveStarted(int reason) {
+ // reason ranges from 1 <-> 3
+ fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_red_dark));
+ Timber.e("OnCameraMoveStarted: %s", REASONS[reason - 1]);
+ }
+ };
+
private class ValueChangeListener implements SeekBar.OnSeekBarChangeListener {
protected TextView textView;
@@ -215,4 +207,50 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe
super.onProgressChanged(seekBar, progress - 180, fromUser);
}
}
+
+ private static class DialogClickListener implements DialogInterface.OnClickListener {
+
+ private MapboxMap mapboxMap;
+ private View dialogContent;
+
+ public DialogClickListener(MapboxMap mapboxMap, View view) {
+ this.mapboxMap = mapboxMap;
+ this.dialogContent = view;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ double latitude = Double.parseDouble(
+ ((TextView) dialogContent.findViewById(R.id.value_lat)).getText().toString());
+ double longitude = Double.parseDouble(
+ ((TextView) dialogContent.findViewById(R.id.value_lon)).getText().toString());
+ double zoom = Double.parseDouble(
+ ((TextView) dialogContent.findViewById(R.id.value_zoom)).getText().toString());
+ double bearing = Double.parseDouble(
+ ((TextView) dialogContent.findViewById(R.id.value_bearing)).getText().toString());
+ double tilt = Double.parseDouble(
+ ((TextView) dialogContent.findViewById(R.id.value_tilt)).getText().toString());
+
+ CameraPosition cameraPosition = new CameraPosition.Builder()
+ .target(new LatLng(latitude, longitude))
+ .zoom(zoom)
+ .bearing(bearing)
+ .tilt(tilt)
+ .build();
+
+ mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000,
+ new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ Timber.v("OnCancel called");
+ }
+
+ @Override
+ public void onFinish() {
+ Timber.v("OnFinish called");
+ }
+ });
+ Timber.v(cameraPosition.toString());
+ }
+ }
}
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 d81538f323..686f564c5c 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
@@ -1,75 +1,118 @@
package com.mapbox.mapboxsdk.testapp.activity.camera;
import android.os.Bundle;
-import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.design.widget.BottomSheetBehavior;
import android.support.v7.app.AppCompatActivity;
+import android.view.View;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
-import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.Style;
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.view.LockableBottomSheetBehavior;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Test activity showcasing using the LatLngBounds camera API.
- * <p>
- * This activity opens the map at zoom level 0 and animates into a bounds set by Los Angeles and New York
- * with some additional padding and an animation duration of 1500 ms.
- * </p>
*/
-public class LatLngBoundsActivity extends AppCompatActivity implements OnMapReadyCallback {
-
- private static final LatLng LOS_ANGELES = new LatLng(34.053940, -118.242622);
- private static final LatLng NEW_YORK = new LatLng(40.712730, -74.005953);
-
- private final LatLng CHINA_BOTTOM_LEFT = new LatLng(15.68169, 73.499857);
- private final LatLng CHINA_TOP_RIGHT = new LatLng(53.560711, 134.77281);
+public class LatLngBoundsActivity extends AppCompatActivity implements View.OnClickListener {
+
+ private static final List<LatLng> LOCATIONS = new ArrayList<LatLng>() {
+ {
+ add(new LatLng(37.806866, -122.422502));
+ add(new LatLng(37.812905, -122.477605));
+ add(new LatLng(37.826944, -122.423188));
+ add(new LatLng(37.752676, -122.447736));
+ add(new LatLng(37.769305, -122.479322));
+ add(new LatLng(37.749834, -122.417867));
+ add(new LatLng(37.756149, -122.405679));
+ add(new LatLng(37.751403, -122.387397));
+ add(new LatLng(37.793064, -122.391517));
+ add(new LatLng(37.769122, -122.427394));
+ }
+ };
+ private static final LatLngBounds BOUNDS = new LatLngBounds.Builder().includes(LOCATIONS).build();
+ private static final int ANIMATION_DURATION_LONG = 450;
+ private static final int ANIMATION_DURATION_SHORT = 250;
+ private static final int BOUNDS_PADDING_DIVIDER_SMALL = 3;
+ private static final int BOUNDS_PADDING_DIVIDER_LARGE = 9;
private MapView mapView;
private MapboxMap mapboxMap;
+ private View bottomSheet;
+ private LockableBottomSheetBehavior bottomSheetBehavior;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_visible_bounds);
-
+ setContentView(R.layout.activity_latlngbounds);
mapView = (MapView) findViewById(R.id.mapView);
- mapView.setStyleUrl(Style.DARK);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(this);
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ initMap();
+ });
+ }
+
+ private void initMap() {
+ addMarkers();
+ initFab();
+ initBottomSheet();
+ moveToBounds(bottomSheet.getMeasuredHeight(), BOUNDS_PADDING_DIVIDER_SMALL, 0);
+ }
+
+ private void addMarkers() {
+ for (LatLng location : LOCATIONS) {
+ mapboxMap.addMarker(new MarkerOptions().position(location));
+ }
+ }
+
+ private void initFab() {
+ findViewById(R.id.fab).setOnClickListener(this);
}
@Override
- public void onMapReady(final MapboxMap map) {
- mapboxMap = map;
- moveToBounds(new LatLngBounds.Builder().include(NEW_YORK).include(LOS_ANGELES).build(), new int[] {0, 0, 0, 0});
- new Handler().postDelayed(new Runnable() {
+ public void onClick(View v) {
+ bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
+ v.animate().alpha(0.0f).setDuration(ANIMATION_DURATION_SHORT);
+ }
+
+ private void initBottomSheet() {
+ bottomSheet = findViewById(R.id.bottom_sheet);
+ bottomSheetBehavior = (LockableBottomSheetBehavior) BottomSheetBehavior.from(bottomSheet);
+ bottomSheetBehavior.setLocked(true);
+ bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
+ @Override
+ public void onStateChanged(@NonNull View bottomSheet, int newState) {
+ if (newState == BottomSheetBehavior.STATE_SETTLING) {
+ moveToBounds(0, BOUNDS_PADDING_DIVIDER_LARGE, ANIMATION_DURATION_LONG);
+ }
+ }
+
@Override
- public void run() {
- moveToBounds(new LatLngBounds.Builder().include(CHINA_BOTTOM_LEFT).include(CHINA_TOP_RIGHT).build(),
- new int[] {100, 100, 100, 100 });
+ public void onSlide(@NonNull View bottomSheet, float slideOffset) {
+
}
- }, 5000);
+ });
}
- private void moveToBounds(LatLngBounds latLngBounds, int[] padding) {
- mapboxMap.clear();
- mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getNorthEast()));
- mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getSouthEast()));
- mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getSouthWest()));
- mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getNorthWest()));
- CameraUpdate update =
- CameraUpdateFactory.newLatLngBounds(latLngBounds,
- padding[0],
- padding[1],
- padding[2],
- padding[3]);
- mapboxMap.moveCamera(update);
+ private void moveToBounds(int verticalOffset, int boundsPaddingDivider, int duration) {
+ int paddingHorizontal = mapView.getMeasuredWidth() / boundsPaddingDivider;
+ int paddingVertical = (mapView.getMeasuredHeight() - verticalOffset) / boundsPaddingDivider;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds(
+ BOUNDS,
+ paddingHorizontal,
+ paddingVertical,
+ paddingHorizontal,
+ paddingVertical + verticalOffset),
+ duration
+ );
}
@Override
@@ -97,9 +140,9 @@ public class LatLngBoundsActivity extends AppCompatActivity implements OnMapRead
}
@Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
}
@Override
@@ -109,8 +152,8 @@ public class LatLngBoundsActivity extends AppCompatActivity implements OnMapRead
}
@Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
+ 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/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 014743df96..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;
@@ -13,6 +11,9 @@ import com.mapbox.mapboxsdk.testapp.R;
import timber.log.Timber;
+/**
+ * Test activity showcasing using maximum and minimum zoom levels to restrict camera movement.
+ */
public class MaxMinZoomActivity extends AppCompatActivity implements OnMapReadyCallback {
private MapView mapView;
@@ -34,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 dde22db2db..4cad7593ef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java
@@ -6,13 +6,11 @@ import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CustomLayer;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.model.customlayer.ExampleCustomLayer;
@@ -38,23 +36,17 @@ public class CustomLayerActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.91448, -243.60947), 10));
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.91448, -243.60947), 10));
- }
});
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setColorFilter(ContextCompat.getColor(this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- swapCustomLayer();
- }
+ fab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ swapCustomLayer();
}
});
}
@@ -69,6 +61,7 @@ public class CustomLayerActivity extends AppCompatActivity {
ExampleCustomLayer.createContext(),
ExampleCustomLayer.InitializeFunction,
ExampleCustomLayer.RenderFunction,
+ ExampleCustomLayer.ContextLostFunction, // Optional
ExampleCustomLayer.DeinitializeFunction);
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 9c031ad32a..70d5b53428 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
@@ -9,7 +9,6 @@ import android.widget.Toast;
import com.google.gson.JsonElement;
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;
@@ -36,52 +35,44 @@ 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(String.format("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);
+ });
});
}
private void debugOutput(List<Feature> features) {
- Timber.i(String.format("Got %s features", features.size()));
+ Timber.i("Got %s features", features.size());
for (Feature feature : features) {
if (feature != null) {
- Timber.i(String.format("Got feature %s with %s properties and Geometry %s",
+ 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.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>"
);
if (feature.getProperties() != null) {
for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) {
- Timber.i(String.format("Prop %s - %s", entry.getKey(), entry.getValue()));
+ Timber.i("Prop %s - %s", entry.getKey(), entry.getValue());
}
}
} else {
- // TODO Question: Why not formatting here??
- Timber.i("Got NULL feature %s");
+ Timber.i("Got 0 features");
}
}
}
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 1d15efef84..8c9d056764 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
@@ -9,7 +9,6 @@ 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.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
@@ -41,41 +40,34 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @SuppressWarnings("ConstantConditions")
- @Override
- public void onMapReady(final MapboxMap mapboxMap) {
- QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap;
- 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(String.format("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();
-
- // remove layer / source if already added
- mapboxMap.removeSource("highlighted-shapes-source");
- mapboxMap.removeLayer("highlighted-shapes-layer");
-
- // Add layer / source
- mapboxMap.addSource(
- new GeoJsonSource("highlighted-shapes-source",
- FeatureCollection.fromFeatures(features))
- );
- mapboxMap.addLayer(new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source")
- .withProperties(fillColor(Color.RED)));
- }
- });
- }
+ mapView.getMapAsync(mapboxMap -> {
+ QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap;
+
+ // Add layer / source
+ final GeoJsonSource source = new GeoJsonSource("highlighted-shapes-source");
+ mapboxMap.addSource(source);
+ mapboxMap.addLayer(
+ new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source")
+ .withProperties(fillColor(Color.RED))
+ );
+
+ selectionBox.setOnClickListener(view -> {
+ // Query
+ int top = selectionBox.getTop() - mapView.getTop();
+ int left = selectionBox.getLeft() - mapView.getLeft();
+ RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
+ Timber.i("Querying box %s for buildings", box);
+ List<Feature> features = mapboxMap.queryRenderedFeatures(box, Filter.lt("height", 10), "building");
+
+ // Show count
+ Toast.makeText(
+ QueryRenderedFeaturesBoxHighlightActivity.this,
+ String.format("%s features in box", features.size()),
+ Toast.LENGTH_SHORT).show();
+
+ // Update source data
+ source.setGeoJson(FeatureCollection.fromFeatures(features));
+ });
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java
index 8c2f3a8fb5..9bad5f3e62 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
@@ -3,26 +3,19 @@ package com.mapbox.mapboxsdk.testapp.activity.feature;
import android.graphics.BitmapFactory;
import android.graphics.RectF;
import android.os.Bundle;
-import android.support.annotation.RawRes;
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.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.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
import java.util.List;
import timber.log.Timber;
@@ -49,68 +42,45 @@ 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", readRawResource(R.raw.test_points_utrecht)));
- } catch (IOException ioException) {
- Timber.e("Could not load geojson: " + ioException.getMessage());
- 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(String.format("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();
+ });
});
}
- private String readRawResource(@RawRes int rawResource) throws IOException {
- InputStream is = getResources().openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- 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();
- }
-
- return writer.toString();
- }
-
public MapboxMap getMapboxMap() {
return mapboxMap;
}
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 8b83db3069..150b081f7f 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
@@ -14,10 +14,8 @@ import android.widget.TextView;
import com.google.gson.JsonElement;
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;
@@ -45,61 +43,54 @@ 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(String.format(
- "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);
+ });
});
}
private void debugOutput(List<Feature> features) {
- Timber.i(String.format("Got %s features", features.size()));
+ Timber.i("Got %s features", features.size());
for (Feature feature : features) {
if (feature != null) {
- Timber.i(String.format("Got feature %s with %s properties and Geometry %s",
+ 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.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>"
);
if (feature.getProperties() != null) {
for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) {
- Timber.i(String.format("Prop %s - %s", entry.getKey(), entry.getValue()));
+ Timber.i("Prop %s - %s", entry.getKey(), entry.getValue());
}
}
} else {
- // TODO Question: Why not formatting here??
- Timber.i("Got NULL feature %s");
+ Timber.i("Got NULL feature");
}
}
}
@@ -185,7 +176,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
private final List<Feature> features;
- public CustomMarker(BaseMarkerOptions baseMarkerOptions, List<Feature> features) {
+ CustomMarker(BaseMarkerOptions baseMarkerOptions, List<Feature> features) {
super(baseMarkerOptions);
this.features = features;
}
@@ -201,7 +192,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
return this;
}
- public CustomMarkerOptions() {
+ CustomMarkerOptions() {
}
private CustomMarkerOptions(Parcel in) {
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..c8bef26856 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,15 +1,12 @@
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.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;
@@ -38,32 +35,26 @@ public class QuerySourceFeaturesActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(final MapboxMap mapboxMap) {
- QuerySourceFeaturesActivity.this.mapboxMap = mapboxMap;
-
- JsonObject properties = new JsonObject();
- properties.addProperty("key1", "value1");
- final GeoJsonSource source = new GeoJsonSource("test-source",
- FeatureCollection.fromFeatures(new Feature[] {
- Feature.fromGeometry(Point.fromCoordinates(new double[] {0, 0}), properties)
- }));
- mapboxMap.addSource(source);
-
- mapboxMap.addLayer(new CircleLayer("test-layer", source.getId()).withFilter(Filter.neq("key1", "value1")));
-
- // Add a click listener
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- // Query
- List<Feature> features = source.querySourceFeatures(Filter.eq("key1", "value1"));
- Toast.makeText(QuerySourceFeaturesActivity.this, String.format("Found %s features",
- features.size()), Toast.LENGTH_SHORT).show();
- }
- });
- }
+ mapView.getMapAsync(mapboxMap -> {
+ QuerySourceFeaturesActivity.this.mapboxMap = mapboxMap;
+
+ JsonObject properties = new JsonObject();
+ properties.addProperty("key1", "value1");
+ final GeoJsonSource source = new GeoJsonSource("test-source",
+ FeatureCollection.fromFeatures(new Feature[] {
+ Feature.fromGeometry(Point.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(point -> {
+ // Query
+ List<Feature> features = source.querySourceFeatures(Filter.eq("key1", "value1"));
+ Toast.makeText(QuerySourceFeaturesActivity.this, String.format("Found %s features",
+ features.size()), Toast.LENGTH_SHORT).show();
+ });
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java
index eec440afb0..2305fdf16c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java
@@ -1,7 +1,7 @@
package com.mapbox.mapboxsdk.testapp.activity.fragment;
-import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.testapp.R;
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java
index ee16b251c5..4f828060ee 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java
@@ -50,7 +50,7 @@ public class SupportMapFragmentActivity extends AppCompatActivity implements OnM
.zoom(11)
.build());
- mapFragment = SupportMapFragment.newInstance();
+ mapFragment = SupportMapFragment.newInstance(options);
transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map");
transaction.commit();
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 8025832429..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
@@ -31,11 +31,11 @@ public class ViewPagerActivity extends AppCompatActivity {
}
}
- public static class MapFragmentAdapter extends FragmentPagerAdapter {
+ static class MapFragmentAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
- public MapFragmentAdapter(FragmentManager fragmentManager) {
+ MapFragmentAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
@@ -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 2be47b4e25..62d3ad34df 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java
@@ -1,6 +1,5 @@
package com.mapbox.mapboxsdk.testapp.activity.imagegenerator;
-import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
@@ -15,6 +14,8 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+import java.util.Locale;
+
/**
* Test activity showcasing the Snapshot API to create and display a bitmap of the current shown Map.
*/
@@ -47,18 +48,15 @@ public class SnapshotActivity extends AppCompatActivity implements OnMapReadyCal
@Override
public void onClick(View view) {
final long startTime = System.nanoTime();
- mapboxMap.snapshot(new MapboxMap.SnapshotReadyCallback() {
- @Override
- public void onSnapshotReady(Bitmap snapshot) {
- long endTime = System.nanoTime();
- long duration = (long) ((endTime - startTime) / 1e6);
- ImageView snapshotView = (ImageView) findViewById(R.id.imageView);
- snapshotView.setImageBitmap(snapshot);
- Toast.makeText(
- SnapshotActivity.this,
- String.format("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 18db2ba8a8..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,15 +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.annotation.Nullable;
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;
@@ -21,16 +17,18 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
+import java.util.Locale;
+
/**
* Test activity showcasing how to dynamically update InfoWindow when Using an MapboxMap.InfoWindowAdapter.
*/
public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implements OnMapReadyCallback {
+ private static final LatLng PARIS = new LatLng(48.864716, 2.349014);
+
private MapboxMap mapboxMap;
private MapView mapView;
- private LatLng paris = new LatLng(48.864716, 2.349014);
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -43,7 +41,6 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen
@Override
public void onMapReady(MapboxMap map) {
-
mapboxMap = map;
// Add info window adapter
@@ -57,34 +54,33 @@ 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
- 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) infoWindow.getView()).setText(String.format("%.2fkm", distanceKm));
-
+ 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();
- }
+ });
}
});
// Focus on Paris
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(paris));
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(PARIS));
}
private MarkerView addMarker(MapboxMap mapboxMap) {
return mapboxMap.addMarker(
new MarkerViewOptions()
- .position(paris)
+ .position(PARIS)
.icon(IconUtils.drawableToIcon(this, R.drawable.ic_location_city,
ResourcesCompat.getColor(getResources(), R.color.mapbox_blue, getTheme()))
));
@@ -92,19 +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() {
-
- @Nullable
- @Override
- public View getInfoWindow(@NonNull Marker marker) {
- TextView textView = new TextView(DynamicInfoWindowAdapterActivity.this);
- textView.setText(marker.getTitle());
- textView.setBackgroundColor(Color.WHITE);
- textView.setText("Click the map to calculate the 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 7026a797d5..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,12 +12,14 @@ 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;
import com.mapbox.mapboxsdk.testapp.utils.IconUtils;
+/**
+ * Test activity showcasing using an InfoWindowAdapter to provide a custom InfoWindow content.
+ */
public class InfoWindowAdapterActivity extends AppCompatActivity {
private MapView mapView;
@@ -30,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
new file mode 100644
index 0000000000..3b58843e13
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java
@@ -0,0 +1,281 @@
+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;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+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.mapboxsdk.utils.MapFragmentUtils;
+
+/**
+ * Test activity showcasing using a bottomView with a MapView and stacking map fragments below.
+ */
+public class BottomSheetActivity extends AppCompatActivity {
+
+ private static final String TAG_MAIN_FRAGMENT = "com.mapbox.mapboxsdk.fragment.tag.main";
+ private static final String TAG_BOTTOM_FRAGMENT = "com.mapbox.mapboxsdk.fragment.tag.bottom";
+
+ private boolean bottomSheetFragmentAdded;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_bottom_sheet);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ findViewById(R.id.fabFragment).setOnClickListener(v -> addMapFragment());
+
+ findViewById(R.id.fabBottomSheet).setOnClickListener(v -> toggleBottomSheetMapFragment());
+
+ BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
+ bottomSheetBehavior.setPeekHeight((int) (64 * getResources().getDisplayMetrics().density));
+ bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
+ toggleBottomSheetMapFragment();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ FragmentManager fragmentManager = getSupportFragmentManager();
+
+ if (fragmentManager.getBackStackEntryCount() > 0) {
+ fragmentManager.popBackStack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ private void addMapFragment() {
+ 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(String.valueOf(mainMapFragment.hashCode()));
+ fragmentTransaction.commit();
+ Toast.makeText(this, "Amount of main map fragments: " + (fragmentCount + 1), Toast.LENGTH_SHORT).show();
+ }
+
+ private void toggleBottomSheetMapFragment() {
+ if (!bottomSheetFragmentAdded) {
+ addBottomSheetMapFragment();
+ } else {
+ removeBottomSheetFragment();
+ }
+ bottomSheetFragmentAdded = !bottomSheetFragmentAdded;
+ }
+
+ private void addBottomSheetMapFragment() {
+ FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
+ fragmentTransaction.add(R.id.fragment_container_bottom, BottomSheetFragment.newInstance(), TAG_BOTTOM_FRAGMENT);
+ fragmentTransaction.commit();
+ }
+
+ private void removeBottomSheetFragment() {
+ Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_BOTTOM_FRAGMENT);
+ if (fragment != null) {
+ getSupportFragmentManager().beginTransaction().remove(fragment).commit();
+ }
+ }
+
+ public static class MainMapFragment extends Fragment implements OnMapReadyCallback {
+
+ private static final String[] STYLES = new String[] {
+ Style.MAPBOX_STREETS,
+ Style.SATELLITE_STREETS,
+ Style.LIGHT,
+ Style.DARK,
+ Style.SATELLITE,
+ Style.OUTDOORS
+ };
+
+ private MapView map;
+
+ public static MainMapFragment newInstance(int mapCounter) {
+ MainMapFragment mapFragment = new MainMapFragment();
+ MapboxMapOptions mapboxMapOptions = new MapboxMapOptions();
+ mapboxMapOptions.styleUrl(STYLES[Math.min(Math.max(mapCounter, 0), STYLES.length - 1)]);
+ mapFragment.setArguments(MapFragmentUtils.createFragmentArgs(mapboxMapOptions));
+ return mapFragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ Context context = inflater.getContext();
+ return map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ map.onCreate(savedInstanceState);
+ map.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ Location location = mapboxMap.getMyLocation();
+ if (location != null) {
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15));
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ map.onStart();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ map.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ map.onPause();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ map.onStop();
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ map.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ map.onLowMemory();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ map.onDestroy();
+ }
+ }
+
+ public static class BottomSheetFragment extends Fragment implements OnMapReadyCallback {
+
+ private MapView map;
+
+ 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));
+ return mapFragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ Context context = inflater.getContext();
+ return map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ map.onCreate(savedInstanceState);
+ map.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ Location location = mapboxMap.getMyLocation();
+ if (location != null) {
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15));
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ map.onStart();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ map.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ map.onPause();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ map.onStop();
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ map.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ map.onLowMemory();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ map.onDestroy();
+ }
+ }
+}
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 18d6fadcb8..22da952560 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
@@ -1,29 +1,43 @@
package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+import android.content.Context;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
+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;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.Property;
import com.mapbox.mapboxsdk.testapp.R;
+import java.util.List;
+import java.util.Locale;
+
import timber.log.Timber;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+
/**
- * Test Activity showcasing the different debug modes and allows to cycle between the default map styles.
+ * Test activity showcasing the different debug modes and allows to cycle between the default map styles.
*/
-public class DebugModeActivity extends AppCompatActivity {
+public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCallback {
private MapView mapView;
private MapboxMap mapboxMap;
-
+ private ActionBarDrawerToggle actionBarDrawerToggle;
private int currentStyleIndex = 0;
private static final String[] STYLES = new String[] {
@@ -32,70 +46,131 @@ public class DebugModeActivity extends AppCompatActivity {
Style.LIGHT,
Style.DARK,
Style.SATELLITE,
- Style.SATELLITE_STREETS
+ Style.SATELLITE_STREETS,
+ Style.TRAFFIC_DAY,
+ Style.TRAFFIC_NIGHT
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_debug_mode);
+ setupToolbar();
+ setupMapView(savedInstanceState);
+ setupDebugChangeView();
+ setupStyleChangeView();
+ }
+
+ private void setupToolbar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+ DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ actionBarDrawerToggle = new ActionBarDrawerToggle(this,
+ drawerLayout,
+ R.string.navigation_drawer_open,
+ R.string.navigation_drawer_close
+ );
+ actionBarDrawerToggle.setDrawerIndicatorEnabled(true);
+ actionBarDrawerToggle.syncState();
+ }
+ }
+
+ private void setupMapView(Bundle savedInstanceState) {
mapView = (MapView) findViewById(R.id.mapView);
+ 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());
+ }
+ });
+
mapView.setTag(true);
mapView.setStyleUrl(STYLES[currentStyleIndex]);
mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap map) {
- mapboxMap = map;
-
- mapboxMap.getUiSettings().setZoomControlsEnabled(true);
-
- // show current zoom level on screen
- 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 void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+ mapboxMap.getUiSettings().setZoomControlsEnabled(true);
+
+ setupNavigationView(mapboxMap.getLayers());
+ setupZoomView();
+ setFpsView();
+ }
+
+ private void setFpsView() {
+ final TextView fpsView = (TextView) findViewById(R.id.fpsView);
+ 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((parent, view, position, id) -> {
+ Layer clickedLayer = adapter.getItem(position);
+ toggleLayerVisibility(clickedLayer);
+ closeNavigationView();
});
+ }
+ private void toggleLayerVisibility(Layer layer) {
+ boolean isVisible = layer.getVisibility().getValue().equals(Property.VISIBLE);
+ layer.setProperties(
+ visibility(
+ isVisible ? Property.NONE : Property.VISIBLE
+ )
+ );
+ }
+ private void closeNavigationView() {
+ DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ drawerLayout.closeDrawers();
+ }
+
+ private void setupZoomView() {
+ final TextView textView = (TextView) findViewById(R.id.textZoom);
+ 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? " + 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));
}
});
}
@Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return actionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
+ }
+
+ @Override
protected void onStart() {
super.onStart();
mapView.onStart();
@@ -136,4 +211,58 @@ public class DebugModeActivity extends AppCompatActivity {
super.onLowMemory();
mapView.onLowMemory();
}
+
+ private static class LayerListAdapter extends BaseAdapter {
+
+ private LayoutInflater layoutInflater;
+ private List<Layer> layers;
+
+ LayerListAdapter(Context context, List<Layer> layers) {
+ this.layoutInflater = LayoutInflater.from(context);
+ this.layers = layers;
+ }
+
+ @Override
+ public int getCount() {
+ return layers.size();
+ }
+
+ @Override
+ public Layer getItem(int position) {
+ return layers.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Layer layer = layers.get(position);
+ View view = convertView;
+ if (view == null) {
+ view = layoutInflater.inflate(android.R.layout.simple_list_item_2, parent, false);
+ ViewHolder holder = new ViewHolder(
+ (TextView) view.findViewById(android.R.id.text1),
+ (TextView) view.findViewById(android.R.id.text2)
+ );
+ view.setTag(holder);
+ }
+ ViewHolder holder = (ViewHolder) view.getTag();
+ holder.text.setText(layer.getClass().getSimpleName());
+ holder.subText.setText(layer.getId());
+ return view;
+ }
+
+ private static class ViewHolder {
+ final TextView text;
+ final TextView subText;
+
+ ViewHolder(TextView text, TextView subText) {
+ this.text = text;
+ this.subText = subText;
+ }
+ }
+ }
}
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 7ec9cb9242..75b2378ef7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java
@@ -3,26 +3,28 @@ 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;
import android.view.LayoutInflater;
-import android.view.SurfaceView;
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;
+/**
+ * Test activity showcasing having 2 maps on top of each other.
+ * <p>
+ * The small map is using the `mapbox_enableZMediaOverlay="true"` configuration
+ * </p>
+ */
public class DoubleMapActivity extends AppCompatActivity {
private static final String TAG_FRAGMENT = "map";
@@ -45,11 +47,11 @@ public class DoubleMapActivity extends AppCompatActivity {
public void setMapboxMap(MapboxMap map) {
// we need to set mapboxmap on the parent activity,
// for auto-generated ui tests
-
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) {
@@ -58,6 +60,9 @@ public class DoubleMapActivity extends AppCompatActivity {
}
}
+ /**
+ * Custom fragment containing 2 MapViews.
+ */
public static class DoubleMapFragment extends Fragment {
private DoubleMapActivity activity;
@@ -82,50 +87,39 @@ 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 {
- TrackingSettings settings = mapboxMap.getTrackingSettings();
- settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- } catch (SecurityException securityException) {
- // permission is handled in MainActivity
- getActivity().finish();
- }
-
- mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
- @Override
- public void onMapClick(@NonNull LatLng point) {
- // test if we can open 2 activities after each other
- startActivity(new Intent(mapViewMini.getContext(), DoubleMapActivity.class));
- }
- });
+ mapViewMini.getMapAsync(mapboxMap -> {
+ mapboxMap.setStyleUrl(Style.LIGHT);
+ mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4));
+
+ UiSettings uiSettings = mapboxMap.getUiSettings();
+ uiSettings.setAllGesturesEnabled(false);
+ uiSettings.setCompassEnabled(false);
+ uiSettings.setAttributionEnabled(false);
+ uiSettings.setLogoEnabled(false);
+
+ try {
+ mapboxMap.setMyLocationEnabled(true);
+ TrackingSettings settings = mapboxMap.getTrackingSettings();
+ settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
+ } catch (SecurityException securityException) {
+ // permission is handled in MainActivity
+ getActivity().finish();
}
- });
- SurfaceView surfaceViewMini = (SurfaceView) mapViewMini.findViewById(R.id.surfaceView);
- surfaceViewMini.setZOrderMediaOverlay(true);
+ mapboxMap.setOnMapClickListener(point -> {
+ // test if we can open 2 activities after each other
+ startActivity(new Intent(mapViewMini.getContext(), DoubleMapActivity.class));
+ });
+ });
}
@Override
@@ -157,8 +151,8 @@ public class DoubleMapActivity extends AppCompatActivity {
}
@Override
- public void onDestroy() {
- super.onDestroy();
+ public void onDestroyView() {
+ super.onDestroyView();
mapView.onDestroy();
mapViewMini.onDestroy();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java
index 9ac87deb0d..24a167e260 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java
@@ -16,7 +16,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
- * Test Activity showcasing restricting user gestures to a bounds around Iceland.
+ * Test activity showcasing restricting user gestures to a bounds around Iceland.
*/
public class LatLngBoundsForCameraActivity extends AppCompatActivity implements OnMapReadyCallback {
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/SimpleWearMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java
index f5bca0e051..18d675a289 100644
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/SimpleWearMapActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java
@@ -1,29 +1,42 @@
-package com.mapbox.weartestapp.activity;
+package com.mapbox.mapboxsdk.testapp.activity.maplayout;
import android.os.Bundle;
-import android.support.wearable.activity.WearableActivity;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+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.weartestapp.R;
-
-public class SimpleWearMapActivity extends WearableActivity {
+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;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_simple_mapview);
+ setContentView(R.layout.activity_local_glyph);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
- public void onMapReady(MapboxMap mapboxMap) {
-
- // Customize map with markers, polylines, etc.
+ public void onMapReady(@NonNull MapboxMap mapboxMap) {
+ // Set initial position to Suzhou
+ mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(31.3003, 120.7457))
+ .zoom(11)
+ .bearing(0)
+ .tilt(0)
+ .build()));
}
});
}
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
new file mode 100644
index 0000000000..160e69ed6e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java
@@ -0,0 +1,102 @@
+package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+
+import android.os.Bundle;
+import android.support.v4.util.LongSparseArray;
+import android.support.v7.app.AppCompatActivity;
+
+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.testapp.R;
+
+import timber.log.Timber;
+
+/**
+ * Test activity showcasing how to listen to map change events.
+ */
+public class MapChangeActivity extends AppCompatActivity {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_simple);
+
+ final LongSparseArray<String> mapChangeMap = buildMapChangeStringValueSparseArray();
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.addOnMapChangedListener(change -> Timber.e("OnMapChange: %s, %s", change, mapChangeMap.get(change)));
+
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(55.754020, 37.620948), 12), 9000);
+ });
+ }
+
+ private LongSparseArray<String> buildMapChangeStringValueSparseArray() {
+ LongSparseArray<String> mapChangeArray = new LongSparseArray<>();
+ mapChangeArray.put(MapView.REGION_WILL_CHANGE, "Region will change");
+ mapChangeArray.put(MapView.REGION_WILL_CHANGE_ANIMATED, "Region will change animated");
+ mapChangeArray.put(MapView.REGION_IS_CHANGING, "Region is changing");
+ mapChangeArray.put(MapView.REGION_DID_CHANGE, "Region did change");
+ mapChangeArray.put(MapView.REGION_DID_CHANGE_ANIMATED, "Region did change animated");
+ mapChangeArray.put(MapView.WILL_START_LOADING_MAP, "Will start loading map");
+ mapChangeArray.put(MapView.DID_FINISH_LOADING_MAP, "Did finish loading map");
+ mapChangeArray.put(MapView.DID_FAIL_LOADING_MAP, "Did fail loading map");
+ mapChangeArray.put(MapView.WILL_START_RENDERING_FRAME, "Will start rendering frame");
+ mapChangeArray.put(MapView.DID_FINISH_RENDERING_FRAME, "Did finish rendering frame");
+ mapChangeArray.put(MapView.DID_FINISH_RENDERING_FRAME_FULLY_RENDERED, "Did finish rendering frame fully rendered");
+ mapChangeArray.put(MapView.WILL_START_RENDERING_MAP, "Will start rendering map");
+ mapChangeArray.put(MapView.DID_FINISH_RENDERING_MAP, "Did finish rendering map");
+ mapChangeArray.put(MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED, "Did finish rendering map fully rendered");
+ mapChangeArray.put(MapView.DID_FINISH_LOADING_STYLE, "Did finish loading style");
+ mapChangeArray.put(MapView.SOURCE_DID_CHANGE, "Source did change");
+ return mapChangeArray;
+ }
+
+ @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 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);
+ }
+}
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 495360a168..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
@@ -1,6 +1,8 @@
package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+import android.app.Dialog;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
@@ -27,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");
});
}
@@ -65,6 +64,22 @@ public class MapInDialogActivity extends AppCompatActivity {
mapView.onCreate(savedInstanceState);
}
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new Dialog(getActivity(), getTheme()) {
+ boolean destroyed = false;
+ @Override
+ public void dismiss() {
+ if (mapView != null && !destroyed) {
+ mapView.onDestroy();
+ destroyed = true;
+ }
+ super.dismiss();
+ }
+ };
+ }
+
@Override
public void onStart() {
super.onStart();
@@ -90,12 +105,6 @@ public class MapInDialogActivity extends AppCompatActivity {
}
@Override
- public 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/maplayout/MapPaddingActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java
index 0cf0191cea..9a6079b157 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java
@@ -1,7 +1,6 @@
package com.mapbox.mapboxsdk.testapp.activity.maplayout;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@@ -13,7 +12,6 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.TrackingSettings;
import com.mapbox.mapboxsdk.testapp.R;
@@ -37,19 +35,16 @@ public class MapPaddingActivity extends AppCompatActivity {
mapView.setTag(true);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- MapPaddingActivity.this.mapboxMap = mapboxMap;
+ mapView.getMapAsync(mapboxMap -> {
+ MapPaddingActivity.this.mapboxMap = mapboxMap;
- int paddingLeft = (int) getResources().getDimension(R.dimen.map_padding_left);
- int paddingBottom = (int) getResources().getDimension(R.dimen.map_padding_bottom);
- int paddingRight = (int) getResources().getDimension(R.dimen.map_padding_right);
- int paddingTop = (int) getResources().getDimension(R.dimen.map_padding_top);
- mapboxMap.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+ int paddingLeft = (int) getResources().getDimension(R.dimen.map_padding_left);
+ int paddingBottom = (int) getResources().getDimension(R.dimen.map_padding_bottom);
+ int paddingRight = (int) getResources().getDimension(R.dimen.map_padding_right);
+ int paddingTop = (int) getResources().getDimension(R.dimen.map_padding_top);
+ mapboxMap.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
- moveToBangalore();
- }
+ moveToBangalore();
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java
deleted file mode 100644
index 888482b219..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java
+++ /dev/null
@@ -1,252 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.maplayout;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.support.design.widget.Snackbar;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapFragment;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.R;
-
-public class NavigationDrawerActivity extends AppCompatActivity {
-
- private boolean firstStyle = true;
- private MapboxMap mapboxMap;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_navigation_drawer);
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- NavigationDrawerFragment navigationDrawerFragment;
-
- getFragmentManager()
- .beginTransaction()
- .add(R.id.navigation_drawer, navigationDrawerFragment = new NavigationDrawerFragment())
- .commit();
-
- navigationDrawerFragment.setUp(this,
- R.id.navigation_drawer,
- (DrawerLayout) findViewById(R.id.drawer_layout),
- getSupportActionBar());
- }
-
- public void onNavigationDrawerItemSelected(int position) {
- // update the main content by replacing fragments
- switch (position) {
- case 0:
- // options
- MapboxMapOptions options = new MapboxMapOptions();
- options.styleUrl(firstStyle ? Style.LIGHT : Style.SATELLITE);
- options.camera(new CameraPosition.Builder()
- .target(new LatLng(39.913271, 116.413891))
- .zoom(12)
- .build());
-
- // fragment
- MapFragment mapFragment = MapFragment.newInstance(options);
- FragmentManager fragmentManager = getFragmentManager();
- fragmentManager.beginTransaction()
- .replace(R.id.container, mapFragment)
- .commit();
-
- // get callback when map is ready
- mapFragment.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- }
- });
-
- firstStyle = !firstStyle;
- break;
- case 1:
- Snackbar.make(
- findViewById(android.R.id.content),
- "Hello from snackbar",
- Snackbar.LENGTH_LONG)
- .show();
- break;
- }
- }
-
- public static class NavigationDrawerFragment extends Fragment {
-
- private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";
- private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned";
-
- private ActionBarDrawerToggle drawerToggle;
-
- private DrawerLayout drawerLayout;
- private ListView drawerListView;
- private View fragmentContainerView;
-
- private int currentSelectedPosition = 0;
- private boolean fromSavedInstanceState;
- private boolean userLearnedDrawer;
-
- public NavigationDrawerFragment() {
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
- userLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
-
- if (savedInstanceState != null) {
- currentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
- fromSavedInstanceState = true;
- }
- selectItem(currentSelectedPosition);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- setHasOptionsMenu(true);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- drawerListView = (ListView) inflater.inflate(
- R.layout.drawer_navigation_drawer, container, false);
- drawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- selectItem(position);
- }
- });
- drawerListView.setAdapter(new ArrayAdapter<>(
- inflater.getContext(),
- android.R.layout.simple_list_item_activated_1,
- android.R.id.text1,
- new String[] {
- "Different style",
- "Show snackbar"
- }));
- drawerListView.setItemChecked(currentSelectedPosition, true);
- return drawerListView;
- }
-
- /**
- * Users of this fragment must call this method to set up the navigation drawer interactions.
- *
- * @param fragmentId The android:id of this fragment in its activity's layout.
- * @param drawerLayout The DrawerLayout containing this fragment's UI.
- */
- public void setUp(Activity activity, int fragmentId, DrawerLayout drawerLayout, ActionBar actionBar) {
- fragmentContainerView = activity.findViewById(fragmentId);
- this.drawerLayout = drawerLayout;
- // drawerLayout.setScrimColor(Color.TRANSPARENT);
-
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setHomeButtonEnabled(true);
-
- drawerToggle = new ActionBarDrawerToggle(
- activity,
- NavigationDrawerFragment.this.drawerLayout,
- R.string.navigation_drawer_open,
- R.string.navigation_drawer_close
- ) {
- @Override
- public void onDrawerClosed(View drawerView) {
- super.onDrawerClosed(drawerView);
- if (!isAdded()) {
- return;
- }
- getActivity().invalidateOptionsMenu();
- }
-
- @Override
- public void onDrawerOpened(View drawerView) {
- super.onDrawerOpened(drawerView);
- if (!isAdded()) {
- return;
- }
-
- if (!userLearnedDrawer) {
- userLearnedDrawer = true;
- SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(getActivity());
- sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
- }
- getActivity().invalidateOptionsMenu();
- }
- };
-
- if (!userLearnedDrawer && !fromSavedInstanceState) {
- this.drawerLayout.openDrawer(fragmentContainerView);
- }
- this.drawerLayout.post(new Runnable() {
- @Override
- public void run() {
- drawerToggle.syncState();
- }
- });
- this.drawerLayout.setDrawerListener(drawerToggle);
- }
-
- private void selectItem(int position) {
- currentSelectedPosition = position;
- if (drawerListView != null) {
- drawerListView.setItemChecked(position, true);
- }
- if (drawerLayout != null) {
- drawerLayout.closeDrawer(fragmentContainerView);
- }
-
- Activity activity = getActivity();
- if (activity != null && activity instanceof NavigationDrawerActivity) {
- NavigationDrawerActivity navActivity = (NavigationDrawerActivity) activity;
- navActivity.onNavigationDrawerItemSelected(position);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(STATE_SELECTED_POSITION, currentSelectedPosition);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- drawerToggle.onConfigurationChanged(newConfig);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (drawerToggle.onOptionsItemSelected(item)) {
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
- }
-}
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
new file mode 100644
index 0000000000..c5d7dfbef7
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java
@@ -0,0 +1,142 @@
+package com.mapbox.mapboxsdk.testapp.activity.maplayout;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.AppCompatActivity;
+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.testapp.R;
+
+/**
+ * Test activity showcasing visibility changes to the mapview.
+ */
+public class VisibilityChangeActivity extends AppCompatActivity {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private Handler handler = new Handler();
+ private Runnable runnable;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_visibility);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
+ new LatLng(55.754020, 37.620948), 12), 9000);
+ });
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ handler.post(runnable = new VisibilityRunner(mapView, findViewById(R.id.viewParent), handler));
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ private static class VisibilityRunner implements Runnable {
+
+ private MapView mapView;
+ private View viewParent;
+ private Handler handler;
+ private int currentStep;
+
+ VisibilityRunner(MapView mapView, View viewParent, Handler handler) {
+ this.mapView = mapView;
+ this.viewParent = viewParent;
+ this.handler = handler;
+ }
+
+ @Override
+ public void run() {
+ if (isViewHiearchyReady()) {
+ if (isEvenStep()) {
+ viewParent.setVisibility(View.VISIBLE);
+ mapView.setVisibility(View.VISIBLE);
+ } else if (isFirstOrThirdStep()) {
+ mapView.setVisibility(getVisibilityForStep());
+ } else if (isFifthOrSeventhStep()) {
+ viewParent.setVisibility(getVisibilityForStep());
+ }
+ updateStep();
+ }
+ handler.postDelayed(this, 1500);
+ }
+
+ private void updateStep() {
+ if (currentStep == 7) {
+ currentStep = 0;
+ } else {
+ currentStep++;
+ }
+ }
+
+ private int getVisibilityForStep() {
+ return (currentStep == 1 || currentStep == 5) ? View.GONE : View.INVISIBLE;
+ }
+
+ private boolean isFifthOrSeventhStep() {
+ return currentStep == 5 || currentStep == 7;
+ }
+
+ private boolean isFirstOrThirdStep() {
+ return currentStep == 1 || currentStep == 3;
+ }
+
+ private boolean isEvenStep() {
+ return currentStep == 0 || currentStep % 2 == 0;
+ }
+
+ private boolean isViewHiearchyReady() {
+ return mapView != null && viewParent != null;
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (runnable != null) {
+ handler.removeCallbacks(runnable);
+ runnable = null;
+ }
+ 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);
+ }
+}
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 344e9e140a..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;
@@ -12,13 +11,11 @@ import android.widget.Toast;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.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;
@@ -75,28 +72,24 @@ public class OfflineActivity extends AppCompatActivity
// state of your app. This will override any checks performed via the ConnectivityManager.
// Mapbox.getInstance().setConnected(false);
Boolean connected = Mapbox.isConnected();
- Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE,
- "Mapbox is connected: %b", connected));
+ Timber.d("Mapbox is connected: %s", connected);
// Set up map
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
@@ -104,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);
@@ -207,7 +190,7 @@ public class OfflineActivity extends AppCompatActivity
@Override
public void onError(String error) {
- Timber.e("Error: " + error);
+ Timber.e("Error: %s" , error);
}
});
}
@@ -223,7 +206,7 @@ public class OfflineActivity extends AppCompatActivity
}
// Start progress bar
- Timber.d("Download started: " + regionName);
+ Timber.d("Download started: %s", regionName);
startProgress();
// Definition
@@ -241,14 +224,14 @@ public class OfflineActivity extends AppCompatActivity
offlineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() {
@Override
public void onCreate(OfflineRegion offlineRegion) {
- Timber.d("Offline region created: " + regionName);
+ Timber.d("Offline region created: %s" , regionName);
OfflineActivity.this.offlineRegion = offlineRegion;
launchDownload();
}
@Override
public void onError(String error) {
- Timber.e("Error: " + error);
+ Timber.e("Error: %s", error);
}
});
}
@@ -266,6 +249,7 @@ public class OfflineActivity extends AppCompatActivity
if (status.isComplete()) {
// Download complete
endProgress("Region downloaded successfully.");
+ offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE);
offlineRegion.setObserver(null);
return;
} else if (status.isRequiredResourceCountPrecise()) {
@@ -283,11 +267,13 @@ public class OfflineActivity extends AppCompatActivity
@Override
public void onError(OfflineRegionError error) {
Timber.e("onError: %s, %s", error.getReason(), error.getMessage());
+ offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE);
}
@Override
public void mapboxTileCountLimitExceeded(long limit) {
Timber.e("Mapbox tile count limit exceeded: %s", limit);
+ offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE);
}
});
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
new file mode 100644
index 0000000000..1825a0f1ab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
@@ -0,0 +1,126 @@
+package com.mapbox.mapboxsdk.testapp.activity.snapshot;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.ViewTreeObserver;
+import android.widget.GridLayout;
+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.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import timber.log.Timber;
+
+/**
+ * Test activity showing how to use a the {@link com.mapbox.mapboxsdk.snapshotter.MapSnapshotter}
+ */
+public class MapSnapshotterActivity extends AppCompatActivity {
+
+ private GridLayout grid;
+ private List<MapSnapshotter> snapshotters = new ArrayList<>();
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_snapshotter);
+
+ // Find the grid view and start snapshotting as soon
+ // as the view is measured
+ grid = (GridLayout) findViewById(R.id.snapshot_grid);
+ grid.getViewTreeObserver()
+ .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ //noinspection deprecation
+ grid.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ addSnapshots();
+ }
+ });
+ }
+
+ private void addSnapshots() {
+ Timber.i("Creating snapshotters");
+
+ for (int row = 0; row < grid.getRowCount(); row++) {
+ for (int column = 0; column < grid.getColumnCount(); column++) {
+ startSnapShot(row, column);
+ }
+ }
+ }
+
+ private void startSnapShot(final int row, final int column) {
+
+ // Define the dimensions
+ MapSnapshotter.Options options = new MapSnapshotter.Options(
+ grid.getMeasuredWidth() / grid.getColumnCount(),
+ grid.getMeasuredHeight() / grid.getRowCount()
+ )
+ // Optionally the pixel ratio
+ .withPixelRatio(1)
+
+ // Optionally the style
+ .withStyle((column + row) % 2 == 0 ? Style.TRAFFIC_DAY : Style.DARK);
+
+ // Optionally the visible region
+ if (row % 2 == 0) {
+ options.withRegion(new LatLngBounds.Builder()
+ .include(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160)))
+ .include(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160)))
+ .build()
+ );
+ }
+
+ // Optionally the camera options
+ if (column % 2 == 0) {
+ options.withCameraPosition(new CameraPosition.Builder()
+ .target(options.getRegion() != null
+ ? options.getRegion().getCenter()
+ : new LatLng(randomInRange(-80, 80), randomInRange(-160, 160)))
+ .bearing(randomInRange(0, 360))
+ .tilt(randomInRange(0, 60))
+ .zoom(randomInRange(0, 20))
+ .build()
+ );
+ }
+
+ MapSnapshotter snapshotter = new MapSnapshotter(MapSnapshotterActivity.this, options);
+
+ 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);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ // Make sure to stop the snapshotters on pause
+ for (MapSnapshotter snapshotter : snapshotters) {
+ snapshotter.cancel();
+ }
+ snapshotters.clear();
+ }
+
+ private static Random random = new Random();
+
+ public static float randomInRange(float min, float max) {
+ return (random.nextFloat() * (max - min)) + min;
+ }
+
+}
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
new file mode 100644
index 0000000000..781e7b6334
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java
@@ -0,0 +1,79 @@
+package com.mapbox.mapboxsdk.testapp.activity.snapshot;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.PointF;
+import android.os.Bundle;
+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;
+
+/**
+ * Test activity showing how to use a the {@link MapSnapshotter} and overlay
+ * {@link android.graphics.Bitmap}s on top.
+ */
+public class MapSnapshotterMarkerActivity extends AppCompatActivity implements MapSnapshotter.SnapshotReadyCallback {
+
+ private MapSnapshotter mapSnapshotter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_snapshotter_marker);
+
+ final View container = findViewById(R.id.container);
+ container.getViewTreeObserver()
+ .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ //noinspection deprecation
+ container.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+
+ Timber.i("Starting snapshot");
+
+ mapSnapshotter = new MapSnapshotter(
+ getApplicationContext(),
+ new MapSnapshotter
+ .Options(Math.min(container.getMeasuredWidth(), 1024), Math.min(container.getMeasuredHeight(), 1024))
+ .withStyle(Style.TRAFFIC_DAY)
+ .withCameraPosition(new CameraPosition.Builder().target(new LatLng(52.090737, 5.121420)).zoom(15).build())
+ );
+ mapSnapshotter.start(MapSnapshotterMarkerActivity.this);
+ }
+ });
+ }
+
+ @Override
+ public void onSnapshotReady(MapSnapshot snapshot) {
+ Timber.i("Snapshot ready");
+ ImageView imageView = (ImageView) findViewById(R.id.snapshot_image);
+ Bitmap image = addMarker(snapshot);
+ imageView.setImageBitmap(image);
+ }
+
+ private Bitmap addMarker(MapSnapshot snapshot) {
+ Canvas canvas = new Canvas(snapshot.getBitmap());
+ Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.mapbox_marker_icon_default, null);
+ // 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),
+ 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
new file mode 100644
index 0000000000..ef5913beb0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java
@@ -0,0 +1,106 @@
+package com.mapbox.mapboxsdk.testapp.activity.snapshot;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+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.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.snapshotter.MapSnapshot;
+import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.Random;
+
+/**
+ * Test activity showing how to use a the {@link MapSnapshotter}
+ */
+public class MapSnapshotterReuseActivity extends AppCompatActivity implements MapSnapshotter.SnapshotReadyCallback {
+
+ private MapSnapshotter mapSnapshotter;
+ private View fab;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map_snapshotter_reuse);
+
+ fab = findViewById(R.id.fab);
+ fab.setVisibility(View.INVISIBLE);
+ 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(
+ getApplicationContext(),
+ new MapSnapshotter.Options(512, 512)
+ );
+
+ mapSnapshotter.start(MapSnapshotterReuseActivity.this);
+ }
+
+ @Override
+ public void onSnapshotReady(MapSnapshot snapshot) {
+ fab.setVisibility(View.VISIBLE);
+ ImageView imageView = (ImageView) findViewById(R.id.snapshot_image);
+ imageView.setImageBitmap(snapshot.getBitmap());
+ }
+
+ private LatLngBounds getRandomBounds() {
+ return LatLngBounds.from(
+ randomInRange(-5, 5),
+ randomInRange(-5, 5),
+ randomInRange(5, 10),
+ randomInRange(5, 10)
+ );
+ }
+
+ private CameraPosition getRandomCameraPosition() {
+ return new CameraPosition.Builder()
+ .target(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160)))
+ .zoom(randomInRange(2, 10))
+ .bearing(randomInRange(0, 90))
+ .build();
+ }
+
+ public String getRandomStyle() {
+ switch (random.nextInt(5)) {
+ case 0:
+ return Style.DARK;
+ case 1:
+ return Style.LIGHT;
+ case 2:
+ return Style.MAPBOX_STREETS;
+ case 3:
+ return Style.OUTDOORS;
+ case 4:
+ return Style.SATELLITE_STREETS;
+ default:
+ return Style.TRAFFIC_DAY;
+ }
+ }
+
+ private static Random random = new Random();
+
+ public static float randomInRange(float min, float max) {
+ return (random.nextFloat() * (max - min)) + min;
+ }
+
+}
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 aeb6751b99..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
@@ -1,10 +1,16 @@
package com.mapbox.mapboxsdk.testapp.activity.style;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
+import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngQuad;
import com.mapbox.mapboxsdk.maps.MapView;
@@ -12,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;
/**
@@ -106,17 +111,27 @@ public class AnimatedImageSourceActivity extends AppCompatActivity implements On
private MapboxMap mapboxMap;
private Handler handler;
- private int[] drawables;
+ private Bitmap[] drawables;
private int drawableIndex;
+ Bitmap getBitmap(int resourceId) {
+ Context context = Mapbox.getApplicationContext();
+ Drawable drawable = ContextCompat.getDrawable(context, resourceId);
+ if (drawable instanceof BitmapDrawable) {
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ return bitmapDrawable.getBitmap();
+ }
+ return null;
+ }
+
RefreshImageRunnable(MapboxMap mapboxMap, Handler handler) {
this.mapboxMap = mapboxMap;
this.handler = handler;
- drawables = new int[4];
- drawables[0] = R.drawable.southeast_radar_0;
- drawables[1] = R.drawable.southeast_radar_1;
- drawables[2] = R.drawable.southeast_radar_2;
- drawables[3] = R.drawable.southeast_radar_3;
+ drawables = new Bitmap[4];
+ drawables[0] = getBitmap(R.drawable.southeast_radar_0);
+ drawables[1] = getBitmap(R.drawable.southeast_radar_1);
+ drawables[2] = getBitmap(R.drawable.southeast_radar_2);
+ drawables[3] = getBitmap(R.drawable.southeast_radar_3);
drawableIndex = 1;
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java
index def7d1678a..609910892b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java
@@ -2,15 +2,12 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.functions.Function;
import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
@@ -46,13 +43,10 @@ public class BuildingFillExtrusionActivity extends AppCompatActivity {
setContentView(R.layout.activity_building_layer);
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull final MapboxMap map) {
- mapboxMap = map;
- setupBuildings();
- setupLight();
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ setupBuildings();
+ setupLight();
});
}
@@ -73,24 +67,18 @@ public class BuildingFillExtrusionActivity extends AppCompatActivity {
private void setupLight() {
light = mapboxMap.getLight();
- findViewById(R.id.fabLightPosition).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- isInitPosition = !isInitPosition;
- if (isInitPosition) {
- light.setPosition(new Position(1.5f, 90, 80));
- } else {
- light.setPosition(new Position(1.15f, 210, 30));
- }
+ findViewById(R.id.fabLightPosition).setOnClickListener(v -> {
+ isInitPosition = !isInitPosition;
+ if (isInitPosition) {
+ light.setPosition(new Position(1.5f, 90, 80));
+ } else {
+ light.setPosition(new Position(1.15f, 210, 30));
}
});
- findViewById(R.id.fabLightColor).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- isRedColor = !isRedColor;
- light.setColor(PropertyFactory.colorToRgbaString(isRedColor ? Color.RED : Color.BLUE));
- }
+ findViewById(R.id.fabLightColor).setOnClickListener(v -> {
+ isRedColor = !isRedColor;
+ light.setColor(PropertyFactory.colorToRgbaString(isRedColor ? Color.RED : Color.BLUE));
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
index 2238d1d5fe..6aa8777777 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
@@ -14,7 +13,6 @@ import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
@@ -56,16 +54,12 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull
- final MapboxMap map) {
- mapboxMap = map;
- addBusStopSource();
- addBusStopCircleLayer();
- initFloatingActionButtons();
- isLoadingStyle = false;
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ addBusStopSource();
+ addBusStopCircleLayer();
+ initFloatingActionButtons();
+ isLoadingStyle = false;
});
}
@@ -74,7 +68,7 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
source = new GeoJsonSource("bus_stop",
new URL("https://raw.githubusercontent.com/cheeaun/busrouter-sg/master/data/2/bus-stops.geojson"));
} catch (MalformedURLException exception) {
- Timber.e("That's not an url... ", exception);
+ Timber.e(exception, "That's not an url... ");
}
mapboxMap.addSource(source);
}
@@ -132,7 +126,7 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
new URL("https://gist.githubusercontent.com/tobrun/7fbc0fe7e9ffea509f7608cda2601d5d/raw/"
+ "a4b8cc289020f91fa2e1553524820054395e36f5/line.geojson")));
} catch (MalformedURLException malformedUrlException) {
- Timber.e("That's not an url... ", malformedUrlException);
+ Timber.e(malformedUrlException, "That's not an url... ");
}
}
@@ -161,12 +155,9 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
}
private void loadNewStyle() {
- mapboxMap.setStyleUrl(getNextStyle(), new MapboxMap.OnStyleLoadedListener() {
- @Override
- public void onStyleLoaded(String style) {
- addBusStop();
- isLoadingStyle = false;
- }
+ mapboxMap.setStyleUrl(getNextStyle(), style -> {
+ addBusStop();
+ isLoadingStyle = false;
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java
index 3763b45e7a..8d35e659d3 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,7 +2,6 @@ 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;
@@ -12,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.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
@@ -45,56 +43,53 @@ 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.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())));
}
- });
- }
+ }
+ });
});
}
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 3a5b30f71f..cbac62bcc1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
@@ -2,40 +2,37 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.RawRes;
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;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.style.functions.Function.composite;
-import static com.mapbox.mapboxsdk.style.functions.Function.property;
-import static com.mapbox.mapboxsdk.style.functions.Function.zoom;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.categorical;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.exponential;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.interval;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.step;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillAntialias;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity;
@@ -59,22 +56,32 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(map -> {
+ // Store for later
+ mapboxMap = map;
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- // Store for later
- mapboxMap = map;
+ // Add a parks layer
+ addParksLayer();
+
+ // Add debug overlay
+ setupDebugZoomView();
- // Add a parks layer
- addParksLayer();
+ // Center and Zoom (Amsterdam, zoomed to streets)
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14));
+ });
+ }
- // Center and Zoom (Amsterdam, zoomed to streets)
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14));
+ private void setupDebugZoomView() {
+ final TextView textView = (TextView) findViewById(R.id.textZoom);
+ mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
+ @Override
+ public void onCameraChange(CameraPosition position) {
+ textView.setText(String.format(getString(R.string.debug_zoom), position.zoom));
}
});
}
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_data_driven_style, menu);
@@ -165,12 +172,11 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- zoom(
- exponential(
- stop(1, fillColor(Color.RED)),
- stop(5, fillColor(Color.BLUE)),
- stop(10, fillColor(Color.GREEN))
- ).withBase(0.5f)
+ interpolate(
+ exponential(0.5f), zoom(),
+ stop(1, color(Color.RED)),
+ stop(5, color(Color.BLUE)),
+ stop(10, color(Color.GREEN))
)
)
);
@@ -184,12 +190,11 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- zoom(
- interval(
- stop(1, fillColor(Color.RED)),
- stop(5, fillColor(Color.BLUE)),
- stop(10, fillColor(Color.GREEN))
- )
+ step(zoom(),
+ color(Color.CYAN),
+ stop(1, color(Color.RED)),
+ stop(5, color(Color.BLUE)),
+ stop(10, color(Color.GREEN))
)
)
);
@@ -203,13 +208,12 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- property(
- "stroke-width",
- exponential(
- stop(1f, fillColor(Color.RED)),
- stop(5f, fillColor(Color.BLUE)),
- stop(10f, fillColor(Color.GREEN))
- ).withBase(0.5f)
+ interpolate(
+ exponential(0.5f),
+ get("stroke-width"),
+ stop(1f, color(Color.RED)),
+ stop(5f, color(Color.BLUE)),
+ stop(10f, color(Color.GREEN))
)
)
);
@@ -223,13 +227,13 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- property(
- "name",
- categorical(
- stop("Westerpark", fillColor(Color.RED)),
- stop("Jordaan", fillColor(Color.BLUE)),
- stop("Prinseneiland", fillColor(Color.GREEN))
- ))
+ match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ literal("Jordaan"), color(Color.BLUE),
+ literal("Prinseneiland"), color(Color.GREEN),
+ color(Color.CYAN)
+ )
)
);
@@ -242,9 +246,7 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillOpacity(
- property(
- "fill-opacity",
- Stops.<Float>identity())
+ get("fill-opacity")
)
);
@@ -257,13 +259,13 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- property(
- "stroke-width",
- interval(
- stop(1f, fillColor(Color.RED)),
- stop(5f, fillColor(Color.BLUE)),
- stop(10f, fillColor(Color.GREEN))
- ))
+ step(
+ get("stroke-width"),
+ color(Color.CYAN),
+ stop(1f, color(Color.RED)),
+ stop(2f, color(Color.BLUE)),
+ stop(3f, color(Color.GREEN))
+ )
)
);
@@ -276,16 +278,30 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- composite(
- "stroke-width",
- exponential(
- stop(1, 1, fillColor(Color.RED)),
- stop(10, 2, fillColor(Color.BLUE)),
- stop(22, 3, fillColor(Color.GREEN)),
- stop(1, 1, fillColor(Color.CYAN)),
- stop(10, 2, fillColor(Color.GRAY)),
- stop(22, 3, fillColor(Color.YELLOW))
- ).withBase(1f)
+ interpolate(
+ exponential(1f),
+ zoom(),
+ stop(12, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.RED)),
+ stop(2f, color(Color.WHITE)),
+ stop(3f, color(Color.BLUE))
+ )),
+ stop(15, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.YELLOW)),
+ stop(2f, color(Color.LTGRAY)),
+ stop(3f, color(Color.CYAN))
+ )),
+ stop(18, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.WHITE)),
+ stop(2f, color(Color.GRAY)),
+ stop(3f, color(Color.GREEN)))
+ )
)
)
);
@@ -294,21 +310,36 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
}
private void addCompositeIntervalFunction() {
- Timber.i("Add composite exponential function");
+ Timber.i("Add composite interval function");
FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER);
assert layer != null;
layer.setProperties(
fillColor(
- composite(
- "stroke-width",
- interval(
- stop(1, 1, fillColor(Color.RED)),
- stop(10, 2, fillColor(Color.BLUE)),
- stop(22, 3, fillColor(Color.GREEN)),
- stop(1, 1, fillColor(Color.CYAN)),
- stop(10, 2, fillColor(Color.GRAY)),
- stop(22, 3, fillColor(Color.YELLOW))
+ interpolate(
+ linear(),
+ zoom(),
+ stop(12, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.RED)),
+ stop(2f, color(Color.WHITE)),
+ stop(3f, color(Color.BLUE))
+ )),
+ stop(15, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.YELLOW)),
+ stop(2f, color(Color.LTGRAY)),
+ stop(3f, color(Color.CYAN))
+ )),
+ stop(18, step(
+ get("stroke-width"),
+ color(Color.BLACK),
+ stop(1f, color(Color.WHITE)),
+ stop(2f, color(Color.GRAY)),
+ stop(3f, color(Color.GREEN))
))
+ )
)
);
@@ -321,30 +352,92 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillColor(
- composite(
- "name",
- categorical(
- stop(7f, "Westerpark", fillColor(Color.RED)),
- stop(8f, "Westerpark", fillColor(Color.BLUE)),
- stop(9f, "Westerpark", fillColor(Color.RED)),
- stop(10f, "Westerpark", fillColor(Color.BLUE)),
- stop(11f, "Westerpark", fillColor(Color.RED)),
- stop(12f, "Westerpark", fillColor(Color.BLUE)),
- stop(13f, "Westerpark", fillColor(Color.RED)),
- stop(14f, "Westerpark", fillColor(Color.BLUE)),
- stop(15f, "Westerpark", fillColor(Color.RED)),
- stop(16f, "Westerpark", fillColor(Color.BLUE)),
- stop(17f, "Westerpark", fillColor(Color.RED)),
- stop(18f, "Westerpark", fillColor(Color.BLUE)),
- stop(19f, "Westerpark", fillColor(Color.RED)),
- stop(20f, "Westerpark", fillColor(Color.BLUE)),
- stop(21f, "Westerpark", fillColor(Color.RED)),
- stop(22f, "Westerpark", fillColor(Color.BLUE)),
- stop(14f, "Jordaan", fillColor(Color.GREEN)),
- stop(18f, "Jordaan", fillColor(Color.CYAN)),
- stop(14f, "Prinseneiland", fillColor(Color.WHITE)),
- stop(18f, "Prinseneiland", fillColor(Color.BLACK))
+ step(zoom(),
+ color(Color.BLACK),
+ stop(7f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(8f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(9f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(10f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(11f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(12f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(13f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(14f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ literal("Jordaan"), color(Color.GREEN),
+ literal("PrinsenEiland"), color(Color.WHITE),
+ color(Color.BLACK)
+ )),
+ stop(15f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(16f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(17f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(18f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ literal("Jordaan"), color(Color.CYAN),
+ color(Color.BLACK)
+ )),
+ stop(19f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(20f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
+ )),
+ stop(21f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.RED),
+ color(Color.BLACK)
+ )),
+ stop(22f, match(
+ get("name"),
+ literal("Westerpark"), color(Color.BLUE),
+ color(Color.BLACK)
))
+ )
)
);
@@ -355,7 +448,7 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
// Add a source
Source source;
try {
- source = new GeoJsonSource("amsterdam-parks-source", readRawResource(R.raw.amsterdam));
+ source = new GeoJsonSource("amsterdam-parks-source", ResourceUtils.readRawResource(this, R.raw.amsterdam));
mapboxMap.addSource(source);
} catch (IOException ioException) {
Toast.makeText(
@@ -365,7 +458,6 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
return;
}
-
// Add a fill layer
mapboxMap.addLayer(new FillLayer(AMSTERDAM_PARKS_LAYER, source.getId())
.withProperties(
@@ -375,21 +467,4 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
)
);
}
-
- private String readRawResource(@RawRes int rawResource) throws IOException {
- InputStream is = getResources().openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- 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();
- }
-
- return writer.toString();
- }
}
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 9a7790c6e5..a88a489cb1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@@ -10,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.style.layers.FillExtrusionLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
@@ -26,6 +24,7 @@ import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpa
public class FillExtrusionActivity extends AppCompatActivity {
private MapView mapView;
+ private MapboxMap mapboxMap;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -34,58 +33,55 @@ public class FillExtrusionActivity extends AppCompatActivity {
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull
- final MapboxMap map) {
- Polygon domTower = Polygon.fromCoordinates(new double[][][] {
- new double[][] {
- new double[] {
- 5.12112557888031,
- 52.09071040847704
- },
- new double[] {
- 5.121227502822875,
- 52.09053901776669
- },
- new double[] {
- 5.121484994888306,
- 52.090601641371805
- },
- new double[] {
- 5.1213884353637695,
- 52.090766439912635
- },
- new double[] {
- 5.12112557888031,
- 52.09071040847704
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ Polygon domTower = Polygon.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);
-
- map.addLayer(
- new FillExtrusionLayer("extrusion-layer", source.getId())
- .withProperties(
- fillExtrusionHeight(40f),
- fillExtrusionOpacity(0.5f),
- fillExtrusionColor(Color.RED)
- )
- );
-
- map.animateCamera(
- CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(new LatLng(52.09071040847704, 5.12112557888031))
- .tilt(45.0)
- .zoom(18)
- .build()
- ),
- 10000
- );
- }
+ }
+ });
+
+ GeoJsonSource source = new GeoJsonSource("extrusion-source", domTower);
+ map.addSource(source);
+
+ mapboxMap.addLayer(
+ new FillExtrusionLayer("extrusion-layer", source.getId())
+ .withProperties(
+ fillExtrusionHeight(40f),
+ fillExtrusionOpacity(0.5f),
+ fillExtrusionColor(Color.RED)
+ )
+ );
+
+ mapboxMap.animateCamera(
+ CameraUpdateFactory.newCameraPosition(
+ new CameraPosition.Builder()
+ .target(new LatLng(52.09071040847704, 5.12112557888031))
+ .tilt(45.0)
+ .zoom(18)
+ .build()
+ ),
+ 10000
+ );
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java
index 1ff0b0e8e1..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
@@ -1,14 +1,15 @@
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.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
+/**
+ * Test activity used for instrumentation tests of fill extrusion.
+ */
public class FillExtrusionStyleTestActivity extends AppCompatActivity {
public MapView mapView;
@@ -22,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 80dfe777cb..8664979292 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
@@ -10,7 +10,6 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions;
@@ -50,15 +49,12 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
// noinspection ConstantConditions
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.7749, 122.4194), 0));
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.7749, 122.4194), 0));
- // Add a clustered source with some layers
- addClusteredGeoJsonSource();
- }
+ // Add a clustered source with some layers
+ addClusteredGeoJsonSource();
});
}
@@ -128,7 +124,7 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
)
);
} catch (MalformedURLException malformedUrlException) {
- Timber.e("That's not an url... " + malformedUrlException.getMessage());
+ Timber.e(malformedUrlException,"That's not an url... ");
}
// Add unclustered layer
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..9dda0f8fa2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java
@@ -0,0 +1,151 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.AppCompatActivity;
+
+import com.mapbox.mapboxsdk.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 com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.FeatureCollection;
+import com.mapbox.services.commons.geojson.MultiLineString;
+import com.mapbox.services.commons.models.Position;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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(Position.fromCoordinates(bounds.getLonWest(), y),
+ Position.fromCoordinates(bounds.getLonEast(), y)));
+ }
+ features.add(Feature.fromGeometry(MultiLineString.fromCoordinates(gridLines)));
+
+ gridLines = new ArrayList();
+ for (double x = Math.floor(bounds.getLonWest() / gridSpacing) * gridSpacing;
+ x <= Math.ceil(bounds.getLonEast() / gridSpacing) * gridSpacing; x += gridSpacing) {
+ gridLines.add(Arrays.asList(Position.fromCoordinates(x, bounds.getLatSouth()),
+ Position.fromCoordinates(x, bounds.getLatNorth())));
+ }
+ features.add(Feature.fromGeometry(MultiLineString.fromCoordinates(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/RealTimeGeoJsonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java
index b9f7ebce35..b9f1dfe810 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java
@@ -55,7 +55,7 @@ public class RealTimeGeoJsonActivity extends AppCompatActivity implements OnMapR
try {
mapboxMap.addSource(new GeoJsonSource(ID_GEOJSON_SOURCE, new URL(URL_GEOJSON_SOURCE)));
} catch (MalformedURLException malformedUrlException) {
- Timber.e("Invalid URL", malformedUrlException);
+ Timber.e(malformedUrlException, "Invalid URL");
}
// add layer
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 f6754af0f9..25d1040bf0 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
@@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
-import android.support.annotation.RawRes;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
@@ -13,10 +12,6 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.style.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,25 +27,22 @@ import com.mapbox.mapboxsdk.style.sources.Source;
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.BufferedReader;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.style.functions.Function.zoom;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop;
-import static com.mapbox.mapboxsdk.style.functions.stops.Stops.exponential;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
import static com.mapbox.mapboxsdk.style.layers.Filter.all;
import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
import static com.mapbox.mapboxsdk.style.layers.Filter.gte;
@@ -91,18 +83,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);
});
}
@@ -285,7 +274,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
// Add a source
Source source;
try {
- source = new GeoJsonSource("amsterdam-spots", readRawResource(R.raw.amsterdam));
+ source = new GeoJsonSource("amsterdam-spots", ResourceUtils.readRawResource(this, R.raw.amsterdam));
} catch (IOException ioException) {
Toast.makeText(
RuntimeStyleActivity.this,
@@ -317,12 +306,12 @@ public class RuntimeStyleActivity extends AppCompatActivity {
layer = mapboxMap.getLayerAs("parksLayer");
// And get some properties
PropertyValue<Boolean> fillAntialias = layer.getFillAntialias();
- Timber.d("Fill anti alias: " + fillAntialias.getValue());
+ Timber.d("Fill anti alias: %s", fillAntialias.getValue());
layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP));
PropertyValue<String> fillTranslateAnchor = layer.getFillTranslateAnchor();
- Timber.d("Fill translate anchor: " + fillTranslateAnchor.getValue());
+ Timber.d("Fill translate anchor: %s", fillTranslateAnchor.getValue());
PropertyValue<String> visibility = layer.getVisibility();
- Timber.d("Visibility: " + visibility.getValue());
+ Timber.d("Visibility: %s", visibility.getValue());
// Get a good look at it all
mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12));
@@ -332,7 +321,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
// Load some data
FeatureCollection parks;
try {
- String json = readRawResource(R.raw.amsterdam);
+ String json = ResourceUtils.readRawResource(this, R.raw.amsterdam);
parks = FeatureCollection.fromJson(json);
} catch (IOException ioException) {
Toast.makeText(
@@ -368,32 +357,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.getFeatures().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.getFeatures().get(park));
+ source.setGeoJson(FeatureCollection.fromFeatures(features));
- // Re-post
- animateParksSource(parks, park + 1);
- }
+ // Re-post
+ animateParksSource(parks, park + 1);
}, counter == 0 ? 100 : 1000);
}
@@ -459,49 +445,21 @@ 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: " + stops.getBase());
- Timber.d("Fill color #stops: " + stops.size());
- if (function.getStops() != null) {
- for (Stop<Float, String> stop : stops) {
- Timber.d("Fill color #stops: " + stop);
- }
- }
- }
- }
-
- private String readRawResource(@RawRes int rawResource) throws IOException {
- InputStream is = getResources().openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- 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();
- }
-
- return writer.toString();
}
private void addCustomTileSource() {
@@ -521,28 +479,25 @@ public class RuntimeStyleActivity extends AppCompatActivity {
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(31, -100), 3));
Handler handler = new Handler(getMainLooper());
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mapboxMap == null) {
- return;
- }
+ handler.postDelayed(() -> {
+ if (mapboxMap == null) {
+ return;
+ }
- Timber.d("Styling filtered fill layer");
+ Timber.d("Styling filtered fill layer");
- FillLayer states = (FillLayer) mapboxMap.getLayer("states");
+ FillLayer states = (FillLayer) mapboxMap.getLayer("states");
- if (states != null) {
- states.setFilter(eq("name", "Texas"));
- states.setFillOpacityTransition(new TransitionOptions(2500, 0));
- states.setFillColorTransition(new TransitionOptions(2500, 0));
- states.setProperties(
- fillColor(Color.RED),
- fillOpacity(0.25f)
- );
- } else {
- Toast.makeText(RuntimeStyleActivity.this, "No states layer in this style", Toast.LENGTH_SHORT).show();
- }
+ if (states != null) {
+ states.setFilter(eq("name", "Texas"));
+ states.setFillOpacityTransition(new TransitionOptions(2500, 0));
+ states.setFillColorTransition(new TransitionOptions(2500, 0));
+ states.setProperties(
+ fillColor(Color.RED),
+ fillOpacity(0.25f)
+ );
+ } else {
+ Toast.makeText(RuntimeStyleActivity.this, "No states layer in this style", Toast.LENGTH_SHORT).show();
}
}, 2000);
}
@@ -552,28 +507,25 @@ public class RuntimeStyleActivity extends AppCompatActivity {
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40, -97), 5));
Handler handler = new Handler(getMainLooper());
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mapboxMap == null) {
- return;
- }
+ handler.postDelayed(() -> {
+ if (mapboxMap == null) {
+ return;
+ }
- Timber.d("Styling filtered line layer");
+ Timber.d("Styling filtered line layer");
- LineLayer counties = (LineLayer) mapboxMap.getLayer("counties");
+ LineLayer counties = (LineLayer) mapboxMap.getLayer("counties");
- if (counties != null) {
- counties.setFilter(eq("NAME10", "Washington"));
+ if (counties != null) {
+ counties.setFilter(eq("NAME10", "Washington"));
- counties.setProperties(
- lineColor(Color.RED),
- lineOpacity(0.75f),
- lineWidth(5f)
- );
- } else {
- Toast.makeText(RuntimeStyleActivity.this, "No counties layer in this style", Toast.LENGTH_SHORT).show();
- }
+ counties.setProperties(
+ lineColor(Color.RED),
+ lineOpacity(0.75f),
+ lineWidth(5f)
+ );
+ } else {
+ Toast.makeText(RuntimeStyleActivity.this, "No counties layer in this style", Toast.LENGTH_SHORT).show();
}
}, 2000);
}
@@ -583,27 +535,24 @@ public class RuntimeStyleActivity extends AppCompatActivity {
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40, -97), 5));
Handler handler = new Handler(getMainLooper());
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (mapboxMap == null) {
- return;
- }
+ handler.postDelayed(() -> {
+ if (mapboxMap == null) {
+ return;
+ }
- Timber.d("Styling numeric fill layer");
+ Timber.d("Styling numeric fill layer");
- FillLayer regions = (FillLayer) mapboxMap.getLayer("regions");
+ FillLayer regions = (FillLayer) mapboxMap.getLayer("regions");
- if (regions != null) {
- regions.setFilter(all(gte("HRRNUM", 200), lt("HRRNUM", 300)));
+ if (regions != null) {
+ regions.setFilter(all(gte("HRRNUM", 200), lt("HRRNUM", 300)));
- regions.setProperties(
- fillColor(Color.BLUE),
- fillOpacity(0.5f)
- );
- } else {
- Toast.makeText(RuntimeStyleActivity.this, "No regions layer in this style", Toast.LENGTH_SHORT).show();
- }
+ regions.setProperties(
+ fillColor(Color.BLUE),
+ fillOpacity(0.5f)
+ );
+ } else {
+ Toast.makeText(RuntimeStyleActivity.this, "No regions layer in this style", Toast.LENGTH_SHORT).show();
}
}, 2000);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java
index 910233accf..53f0870d90 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTestActivity.java
@@ -5,7 +5,6 @@ import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.testapp.R;
/**
@@ -24,12 +23,7 @@ public class RuntimeStyleTestActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- RuntimeStyleTestActivity.this.mapboxMap = mapboxMap;
- }
- });
+ mapView.getMapAsync(mapboxMap -> RuntimeStyleTestActivity.this.mapboxMap = mapboxMap);
}
public MapboxMap getMapboxMap() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java
index 5057578731..e51a7ec8a0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java
@@ -6,7 +6,6 @@ import android.support.v7.app.AppCompatActivity;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.sources.VectorSource;
import com.mapbox.mapboxsdk.testapp.R;
@@ -32,23 +31,20 @@ public class RuntimeStyleTimingTestActivity extends AppCompatActivity {
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- RuntimeStyleTimingTestActivity.this.mapboxMap = mapboxMap;
- VectorSource museums = new VectorSource("museums_source", "mapbox://mapbox.2opop9hr");
- mapboxMap.addSource(museums);
-
- CircleLayer museumsLayer = new CircleLayer("museums", "museums_source");
- museumsLayer.setSourceLayer("museum-cusco");
- museumsLayer.setProperties(
- visibility(VISIBLE),
- circleRadius(8f),
- circleColor(Color.argb(1, 55, 148, 179))
- );
-
- mapboxMap.addLayer(museumsLayer);
- }
+ mapView.getMapAsync(mapboxMap -> {
+ RuntimeStyleTimingTestActivity.this.mapboxMap = mapboxMap;
+ VectorSource museums = new VectorSource("museums_source", "mapbox://mapbox.2opop9hr");
+ mapboxMap.addSource(museums);
+
+ CircleLayer museumsLayer = new CircleLayer("museums", "museums_source");
+ museumsLayer.setSourceLayer("museum-cusco");
+ museumsLayer.setProperties(
+ visibility(VISIBLE),
+ circleRadius(8f),
+ circleColor(Color.argb(1, 55, 148, 179))
+ );
+
+ mapboxMap.addLayer(museumsLayer);
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java
index 6906e90f6e..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
@@ -1,35 +1,28 @@
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.annotation.RawRes;
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;
-import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
+import java.lang.ref.WeakReference;
import timber.log.Timber;
/**
- * Test activity showcasing how to use a file:// resource for the style.json
+ * Test activity showcasing how to use a file:// resource for the style.json and how to use MapboxMap#setStyleJson.
*/
public class StyleFileActivity extends AppCompatActivity {
@@ -43,63 +36,88 @@ 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);
- fab.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary));
- fab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Timber.i("Loading style file");
- new CreateStyleFileTask().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());
});
}
/**
+ * Task to read a style file from the raw folder
+ */
+ private static class LoadStyleFileTask extends AsyncTask<Void, Void, String> {
+ private WeakReference<Context> context;
+ private WeakReference<MapboxMap> mapboxMap;
+
+ LoadStyleFileTask(Context context, MapboxMap mapboxMap) {
+ this.context = new WeakReference<>(context);
+ this.mapboxMap = new WeakReference<>(mapboxMap);
+ }
+
+ @Override
+ protected String doInBackground(Void... voids) {
+ String styleJson = "";
+ try {
+ styleJson = ResourceUtils.readRawResource(context.get(), R.raw.sat_style);
+ } catch (Exception exception) {
+ Timber.e(exception, "Can't load local file style");
+ }
+ return styleJson;
+ }
+
+ @Override
+ protected void onPostExecute(String json) {
+ super.onPostExecute(json);
+ Timber.d("Read json, %s", json);
+ MapboxMap mapboxMap = this.mapboxMap.get();
+ if (mapboxMap != null) {
+ mapboxMap.setStyleJson(json);
+ }
+ }
+ }
+
+ /**
* Task to write a style file to local disk and load it in the map view
*/
- private class CreateStyleFileTask extends AsyncTask<Void, Integer, Long> {
+ private static class CreateStyleFileTask extends AsyncTask<Void, Integer, Long> {
private File cacheStyleFile;
+ private WeakReference<Context> context;
+ private WeakReference<MapboxMap> mapboxMap;
+
+ CreateStyleFileTask(Context context, MapboxMap mapboxMap) {
+ this.context = new WeakReference<>(context);
+ this.mapboxMap = new WeakReference<>(mapboxMap);
+ }
@Override
protected Long doInBackground(Void... params) {
try {
cacheStyleFile = File.createTempFile("my-", ".style.json");
cacheStyleFile.createNewFile();
- Timber.i("Writing style file to: " + cacheStyleFile.getAbsolutePath());
- writeToFile(cacheStyleFile, readRawResource(R.raw.local_style));
+ Timber.i("Writing style file to: %s", cacheStyleFile.getAbsolutePath());
+ Context context = this.context.get();
+ if (context != null) {
+ writeToFile(cacheStyleFile, ResourceUtils.readRawResource(context, R.raw.local_style));
+ }
} catch (Exception exception) {
- Toast.makeText(StyleFileActivity.this, "Could not create style file in cache dir", Toast.LENGTH_SHORT).show();
+ Toast.makeText(context.get(), "Could not create style file in cache dir", Toast.LENGTH_SHORT).show();
}
return 1L;
}
protected void onPostExecute(Long result) {
// Actual file:// usage
- mapboxMap.setStyleUrl("file://" + cacheStyleFile.getAbsolutePath());
- }
-
- private String readRawResource(@RawRes int rawResource) throws IOException {
- InputStream is = getResources().openRawResource(rawResource);
- Writer writer = new StringWriter();
- char[] buffer = new char[1024];
- try {
- 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();
+ MapboxMap mapboxMap = this.mapboxMap.get();
+ if (mapboxMap != null) {
+ mapboxMap.setStyleUrl("file://" + cacheStyleFile.getAbsolutePath());
}
-
- return writer.toString();
}
private void writeToFile(File file, String contents) throws IOException {
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
new file mode 100644
index 0000000000..1ef59db9b1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java
@@ -0,0 +1,309 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.os.AsyncTask;
+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 android.widget.TextView;
+import android.widget.Toast;
+
+import com.google.gson.GsonBuilder;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.Filter;
+import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.style.sources.Source;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
+import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.FeatureCollection;
+import com.mapbox.services.commons.geojson.Geometry;
+import com.mapbox.services.commons.geojson.custom.GeometryDeserializer;
+import com.mapbox.services.commons.geojson.custom.PositionDeserializer;
+import com.mapbox.services.commons.models.Position;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.style.expressions.Expression.concat;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.division;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.downcase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.pi;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.product;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.upcase;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_BOTTOM;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_ANCHOR_TOP;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAnchor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize;
+
+/**
+ * Test activity showcasing using a symbol generator that generates Bitmaps from Android SDK Views.
+ */
+public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ 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_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;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_symbol_generator);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(final MapboxMap map) {
+ mapboxMap = map;
+ addSymbolClickListener();
+ new LoadDataTask(this).execute();
+ }
+
+ private void addSymbolClickListener() {
+ 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();
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_generator_symbol, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_action_icon_overlap) {
+ SymbolLayer layer = mapboxMap.getLayerAs(LAYER_ID);
+ layer.setProperties(iconAllowOverlap(!layer.getIconAllowOverlap().getValue()));
+ return true;
+ } else if (item.getItemId() == R.id.menu_action_filter) {
+ SymbolLayer layer = mapboxMap.getLayerAs(LAYER_ID);
+ layer.setFilter(Filter.eq(FEATURE_RANK, 1));
+ //layer.setFilter(eq(get(FEATURE_RANK), 1));
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @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();
+ }
+
+ /**
+ * Utility class to generate Bitmaps for Symbol.
+ * <p>
+ * Bitmaps can be added to the map with {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}
+ * </p>
+ */
+ private static class SymbolGenerator {
+
+ /**
+ * Generate a Bitmap from an Android SDK View.
+ *
+ * @param view the View to be drawn to a Bitmap
+ * @return the generated bitmap
+ */
+ public static Bitmap generate(@NonNull View view) {
+ int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ view.measure(measureSpec, measureSpec);
+
+ int measuredWidth = view.getMeasuredWidth();
+ int measuredHeight = view.getMeasuredHeight();
+
+ view.layout(0, 0, measuredWidth, measuredHeight);
+ Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
+ bitmap.eraseColor(Color.TRANSPARENT);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ return bitmap;
+ }
+ }
+
+ private static class LoadDataTask extends AsyncTask<Void, Void, FeatureCollection> {
+
+ private SymbolGeneratorActivity activity;
+
+ LoadDataTask(SymbolGeneratorActivity activity) {
+ this.activity = activity;
+ }
+
+ @Override
+ protected FeatureCollection doInBackground(Void... params) {
+ try {
+ // read local geojson from raw folder
+ String tinyCountriesJson = ResourceUtils.readRawResource(activity, 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;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(FeatureCollection featureCollection) {
+ super.onPostExecute(featureCollection);
+ if (featureCollection == null || activity == null) {
+ return;
+ }
+
+ activity.onDataLoaded(featureCollection);
+ }
+ }
+
+ public void onDataLoaded(@NonNull FeatureCollection featureCollection) {
+ // add a geojson to the map
+ Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
+ mapboxMap.addSource(source);
+
+ // create layer use
+ mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
+ .withProperties(
+
+ // icon configuration
+ iconImage(get(literal(FEATURE_ID))),
+ iconAllowOverlap(false),
+ iconSize(
+ division(get(literal(FEATURE_RANK)), literal(2))
+ ),
+ iconAnchor(ICON_ANCHOR_BOTTOM),
+ iconOffset(new Float[] {0.0f, -5.0f}),
+
+ // text field configuration
+ textField(
+ concat(
+ upcase(literal("a ")),
+ get(literal(FEATURE_TYPE)),
+ downcase(literal(" IN ")),
+ get(literal(FEATURE_REGION))
+ )
+ ),
+ textSize(
+ product(get(literal(FEATURE_RANK)), pi())
+ ),
+ textAnchor(TEXT_ANCHOR_TOP)
+ )
+ );
+
+ new GenerateSymbolTask(mapboxMap, this).execute(featureCollection);
+ }
+
+ private static class GenerateSymbolTask extends AsyncTask<FeatureCollection, Void, HashMap<String, Bitmap>> {
+
+ private MapboxMap mapboxMap;
+ private Context context;
+
+ GenerateSymbolTask(MapboxMap mapboxMap, Context context) {
+ this.mapboxMap = mapboxMap;
+ this.context = 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));
+ }
+ return imagesMap;
+ }
+
+ @Override
+ protected void onPostExecute(HashMap<String, Bitmap> bitmapHashMap) {
+ super.onPostExecute(bitmapHashMap);
+ mapboxMap.addImages(bitmapHashMap);
+ }
+ }
+} \ No newline at end of file
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..d89d71e604 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
@@ -15,7 +15,6 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
@@ -51,43 +50,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.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);
});
}
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
new file mode 100644
index 0000000000..4a6e62ef7d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java
@@ -0,0 +1,202 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.google.gson.JsonObject;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.style.layers.Property;
+import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.FeatureCollection;
+import com.mapbox.services.commons.geojson.Point;
+import com.mapbox.services.commons.models.Position;
+
+import java.util.List;
+
+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.layers.PropertyFactory.iconAllowOverlap;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+
+/**
+ * Test activity showcasing changing the icon with a zoom function and adding selection state to a SymbolLayer.
+ */
+public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
+
+ private static final String LAYER_ID = "symbolLayer";
+ private static final String SOURCE_ID = "poiSource";
+ 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;
+ private MapboxMap mapboxMap;
+ private GeoJsonSource source;
+ private SymbolLayer layer;
+
+ private boolean isInitialPosition = true;
+ private boolean isSelected = false;
+ private boolean isShowingSymbolLayer = true;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_zoom_symbol_layer);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ updateSource();
+ addLayer();
+ addMapClickListener();
+ });
+ }
+
+ private void updateSource() {
+ FeatureCollection featureCollection = createFeatureCollection();
+ if (source != null) {
+ source.setGeoJson(featureCollection);
+ } else {
+ source = new GeoJsonSource(SOURCE_ID, featureCollection);
+ mapboxMap.addSource(source);
+ }
+ }
+
+ private void toggleSymbolLayerVisibility() {
+ layer.setProperties(
+ visibility(isShowingSymbolLayer ? Property.NONE : Property.VISIBLE)
+ );
+ isShowingSymbolLayer = !isShowingSymbolLayer;
+ }
+
+ private FeatureCollection createFeatureCollection() {
+ Position position = isInitialPosition
+ ? Position.fromCoordinates(-74.01618140, 40.701745)
+ : Position.fromCoordinates(-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);
+ return FeatureCollection.fromFeatures(new Feature[] {feature});
+ }
+
+ private void addLayer() {
+ 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))
+ )
+ )
+ ),
+ iconSize(
+ property(
+ KEY_PROPERTY_SELECTED,
+ categorical(
+ stop(true, iconSize(3.0f)),
+ stop(false, iconSize(1.0f))
+ )
+ )
+ ),
+ iconAllowOverlap(true)
+ );
+ mapboxMap.addLayer(layer);
+ }
+
+ private void addMapClickListener() {
+ 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");
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_symbols, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (mapboxMap != null) {
+ if (item.getItemId() == R.id.menu_action_change_location) {
+ isInitialPosition = !isInitialPosition;
+ updateSource();
+ } else if (item.getItemId() == R.id.menu_action_toggle_source) {
+ toggleSymbolLayerVisibility();
+ }
+ }
+ 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
+ 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/textureview/TextureViewAnimationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java
new file mode 100644
index 0000000000..1c023e5780
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java
@@ -0,0 +1,145 @@
+package com.mapbox.mapboxsdk.testapp.activity.textureview;
+
+import android.animation.ObjectAnimator;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.TextView;
+
+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.testapp.R;
+
+import java.util.Locale;
+
+/**
+ * Test animating a {@link android.view.TextureView} backed map.
+ */
+public class TextureViewAnimationActivity extends AppCompatActivity {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private Handler handler;
+ private Runnable delayed;
+
+ private static LatLng[] PLACES = {
+ new LatLng(37.7749, -122.4194), // SF
+ new LatLng(38.9072, -77.0369), // DC
+ new LatLng(52.3702, 4.8952), // AMS
+ new LatLng(60.1699, 24.9384), // HEL
+ new LatLng(-13.1639, -74.2236), // AYA
+ new LatLng(52.5200, 13.4050), // BER
+ new LatLng(12.9716, 77.5946), // BAN
+ new LatLng(31.2304, 121.4737) // SHA
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_textureview_animate);
+ handler = new Handler(getMainLooper());
+ setupToolbar();
+ setupMapView(savedInstanceState);
+ }
+
+ private void setupToolbar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+ }
+ }
+
+ private void setupMapView(Bundle savedInstanceState) {
+ mapView = (MapView) findViewById(R.id.mapView);
+ 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);
+ });
+ }
+
+ private void flyTo(final MapboxMap mapboxMap, final int place, final double zoom) {
+ mapboxMap.animateCamera(
+ CameraUpdateFactory.newLatLngZoom(PLACES[place], zoom),
+ 10000,
+ new MapboxMap.CancelableCallback() {
+ @Override
+ public void onCancel() {
+ delayed = () -> {
+ delayed = null;
+ flyTo(mapboxMap, place, zoom);
+ };
+ handler.postDelayed(delayed, 2000);
+ }
+
+ @Override
+ public void onFinish() {
+ flyTo(mapboxMap, place == (PLACES.length - 1) ? 0 : place + 1, zoom);
+ }
+ });
+ }
+
+ private void setFpsView(MapboxMap mapboxMap) {
+ final TextView fpsView = (TextView) findViewById(R.id.fpsView);
+ mapboxMap.setOnFpsChangedListener(fps -> fpsView.setText(String.format(Locale.US, "FPS: %4.2f", fps)));
+ }
+
+ @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();
+ if (handler != null && delayed != null) {
+ handler.removeCallbacks(delayed);
+ }
+ }
+
+ @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/textureview/TextureViewDebugModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java
new file mode 100644
index 0000000000..007e6c7f2e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java
@@ -0,0 +1,265 @@
+package com.mapbox.mapboxsdk.testapp.activity.textureview;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+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.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.Property;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.util.List;
+import java.util.Locale;
+
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
+
+/**
+ * Test activity showcasing the different debug modes and allows to cycle between the default map styles.
+ */
+public class TextureViewDebugModeActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private ActionBarDrawerToggle actionBarDrawerToggle;
+ private int currentStyleIndex = 0;
+
+ private static final String[] STYLES = new String[] {
+ Style.MAPBOX_STREETS,
+ Style.OUTDOORS,
+ Style.LIGHT,
+ Style.DARK,
+ Style.SATELLITE,
+ Style.SATELLITE_STREETS,
+ Style.TRAFFIC_DAY,
+ Style.TRAFFIC_NIGHT
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_textureview_debug_mode);
+ setupToolbar();
+ setupMapView(savedInstanceState);
+ setupDebugChangeView();
+ setupStyleChangeView();
+ }
+
+ private void setupToolbar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ actionBarDrawerToggle = new ActionBarDrawerToggle(this,
+ drawerLayout,
+ R.string.navigation_drawer_open,
+ R.string.navigation_drawer_close
+ );
+ actionBarDrawerToggle.setDrawerIndicatorEnabled(true);
+ actionBarDrawerToggle.syncState();
+ }
+ }
+
+ private void setupMapView(Bundle savedInstanceState) {
+ mapView = (MapView) findViewById(R.id.mapView);
+ 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());
+ }
+ });
+
+ mapView.setTag(true);
+ mapView.setStyleUrl(STYLES[currentStyleIndex]);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(MapboxMap map) {
+ mapboxMap = map;
+ mapboxMap.getUiSettings().setZoomControlsEnabled(true);
+
+ setupNavigationView(mapboxMap.getLayers());
+ setupZoomView();
+ setFpsView();
+ }
+
+ private void setFpsView() {
+ final TextView fpsView = (TextView) findViewById(R.id.fpsView);
+ 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((parent, view, position, id) -> {
+ Layer clickedLayer = adapter.getItem(position);
+ toggleLayerVisibility(clickedLayer);
+ closeNavigationView();
+ });
+ }
+
+ private void toggleLayerVisibility(Layer layer) {
+ boolean isVisible = layer.getVisibility().getValue().equals(Property.VISIBLE);
+ layer.setProperties(
+ visibility(
+ isVisible ? Property.NONE : Property.VISIBLE
+ )
+ );
+ }
+
+ private void closeNavigationView() {
+ DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ drawerLayout.closeDrawers();
+ }
+
+ private void setupZoomView() {
+ final TextView textView = (TextView) findViewById(R.id.textZoom);
+ 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(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(view -> {
+ if (mapboxMap != null) {
+ currentStyleIndex++;
+ if (currentStyleIndex == STYLES.length) {
+ currentStyleIndex = 0;
+ }
+ mapboxMap.setStyleUrl(STYLES[currentStyleIndex], style -> Timber.d("Style loaded %s", style));
+ }
+ });
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ return actionBarDrawerToggle.onOptionsItemSelected(item) || 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();
+ }
+
+ private static class LayerListAdapter extends BaseAdapter {
+
+ private LayoutInflater layoutInflater;
+ private List<Layer> layers;
+
+ LayerListAdapter(Context context, List<Layer> layers) {
+ this.layoutInflater = LayoutInflater.from(context);
+ this.layers = layers;
+ }
+
+ @Override
+ public int getCount() {
+ return layers.size();
+ }
+
+ @Override
+ public Layer getItem(int position) {
+ return layers.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Layer layer = layers.get(position);
+ View view = convertView;
+ if (view == null) {
+ view = layoutInflater.inflate(android.R.layout.simple_list_item_2, parent, false);
+ ViewHolder holder = new ViewHolder(
+ (TextView) view.findViewById(android.R.id.text1),
+ (TextView) view.findViewById(android.R.id.text2)
+ );
+ view.setTag(holder);
+ }
+ ViewHolder holder = (ViewHolder) view.getTag();
+ holder.text.setText(layer.getClass().getSimpleName());
+ holder.subText.setText(layer.getId());
+ return view;
+ }
+
+ private static class ViewHolder {
+ final TextView text;
+ final TextView subText;
+
+ ViewHolder(TextView text, TextView subText) {
+ this.text = text;
+ this.subText = subText;
+ }
+ }
+ }
+}
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
new file mode 100644
index 0000000000..788003d867
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java
@@ -0,0 +1,98 @@
+package com.mapbox.mapboxsdk.testapp.activity.textureview;
+
+import android.os.Bundle;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v7.app.ActionBar;
+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.testapp.R;
+
+/**
+ * Test resizing a {@link android.view.TextureView} backed map on the fly.
+ */
+public class TextureViewResizeActivity extends AppCompatActivity {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_textureview_resize);
+ setupToolbar();
+ setupMapView(savedInstanceState);
+ setupFab();
+ }
+
+ private void setupToolbar() {
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+ }
+ }
+
+ private void setupMapView(Bundle savedInstanceState) {
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.getMapAsync(mapboxMap -> TextureViewResizeActivity.this.mapboxMap = mapboxMap);
+ }
+
+ private void setupFab() {
+ FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabResize);
+ 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));
+ }
+ });
+ }
+
+ @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/BaseLocationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
index a8d1772cb2..71b8115d2e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
@@ -1,25 +1,37 @@
package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-import android.Manifest;
-import android.content.pm.PackageManager;
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.v4.app.ActivityCompat;
+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;
-public abstract class BaseLocationActivity extends AppCompatActivity {
+import java.util.List;
- private static final int PERMISSIONS_LOCATION = 0;
+/**
+ * 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 (!PermissionsManager.areLocationPermissionsGranted(this)) {
- ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_LOCATION);
+ if (!isRuntimePermissionsRequired()) {
+ permissionsManager.requestLocationPermissions(this);
} else {
enableLocation(true);
}
@@ -29,16 +41,21 @@ public abstract class BaseLocationActivity extends AppCompatActivity {
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- if (requestCode == PERMISSIONS_LOCATION) {
- if (!isRuntimePermissionsRequired() || isPermissionAccepted(grantResults)) {
- enableLocation(true);
- }
- }
+ 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);
}
- private boolean isPermissionAccepted(int[] grantResults) {
- return grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED;
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
private boolean isRuntimePermissionsRequired() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
index 660404f144..ff2559089c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
@@ -2,46 +2,39 @@ package com.mapbox.mapboxsdk.testapp.activity.userlocation;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
-import android.view.View;
+import android.view.Menu;
+import android.view.MenuItem;
+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;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
+/**
+ * Test activity showcasing using a custom location engine.
+ */
public class CustomLocationEngineActivity extends BaseLocationActivity {
private MapView mapView;
private MapboxMap mapboxMap;
private FloatingActionButton locationToggleFab;
- private LocationEngine locationServices;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_location_engine);
- locationServices = new MockLocationEngine();
-
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- mapboxMap.setLocationSource(locationServices);
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ mapboxMap.setLocationSource(MockLocationEngine.getInstance());
});
locationToggleFab = (FloatingActionButton) findViewById(R.id.fabLocationToggle);
- locationToggleFab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- toggleGps(!mapboxMap.isMyLocationEnabled());
- }
+ locationToggleFab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ enableLocation(!mapboxMap.isMyLocationEnabled());
}
});
}
@@ -57,6 +50,30 @@ public class CustomLocationEngineActivity extends BaseLocationActivity {
}
@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();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java
index b02b35b0d0..f4b54551bf 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java
@@ -1,38 +1,52 @@
package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
import android.location.Location;
-import android.os.Handler;
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
+ * 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, animation -> {
+ for (LocationEngineListener listener : locationListeners) {
+ listener.onLocationChanged((Location) animation.getAnimatedValue());
+ }
+ });
+ }
- // Mocked data
- private static final int UPDATE_INTERVAL_MS = 1000;
- private static final double[][] locations = new double[][] {
- new double[] {39.489309, -0.360415},
- new double[] {39.492469, -0.358777},
- new double[] {40.393285, -3.707260},
- new double[] {40.394374, -3.707767},
- new double[] {40.398012, -3.715943},
- new double[] {40.416913, -3.703861}};
-
- private Handler handler;
- int currentIndex;
-
- public MockLocationEngine() {
- super();
+ 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() {
- currentIndex = 0;
-
// "Connection" is immediate here
for (LocationEngineListener listener : locationListeners) {
listener.onConnected();
@@ -41,7 +55,6 @@ public class MockLocationEngine extends LocationEngine {
@Override
public void deactivate() {
- handler = null;
}
@Override
@@ -51,44 +64,66 @@ public class MockLocationEngine extends LocationEngine {
@Override
public Location getLastLocation() {
- return getNextLocation();
+ return null;
}
@Override
public void requestLocationUpdates() {
- // Fake regular updates with a handler
- handler = new Handler();
- handler.postDelayed(new LocationUpdateRunnable(), UPDATE_INTERVAL_MS);
+ if (!running) {
+ locationAnimator.start();
+ running = true;
+ }
}
@Override
public void removeLocationUpdates() {
- if (handler != null) {
- handler.removeCallbacksAndMessages(null);
+ if (running) {
+ locationAnimator.stop();
+ running = false;
+ Timber.e("LOC %s", counter);
}
}
- private Location getNextLocation() {
- // Build the next location and rotate the index
- Location location = new Location(MockLocationEngine.class.getSimpleName());
- location.setLatitude(locations[currentIndex][0]);
- location.setLongitude(locations[currentIndex][1]);
- currentIndex = (currentIndex == locations.length - 1 ? 0 : currentIndex + 1);
- return location;
+ @Override
+ public Type obtainType() {
+ return Type.MOCK;
}
- private class LocationUpdateRunnable implements Runnable {
- @Override
- public void run() {
- // Notify of an update
- Location location = getNextLocation();
- for (LocationEngineListener listener : locationListeners) {
- listener.onLocationChanged(location);
- }
+ 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());
- if (handler != null) {
- // Schedule the next update
- handler.postDelayed(new LocationUpdateRunnable(), UPDATE_INTERVAL_MS);
+ @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
index 5560f81fa9..000042306f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
@@ -11,11 +11,9 @@ 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.location.LocationSource;
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;
@@ -31,13 +29,10 @@ public class MyLocationDrawableActivity extends BaseLocationActivity implements
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);
-
- // configure MyLocationView drawables
mapboxMapOptions.myLocationForegroundDrawable(ContextCompat.getDrawable(this, R.drawable.ic_android));
mapboxMapOptions.myLocationBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.ic_android));
mapboxMapOptions.myLocationForegroundTintColor(Color.GREEN);
@@ -45,38 +40,24 @@ public class MyLocationDrawableActivity extends BaseLocationActivity implements
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(R.id.container);
+ 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);
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ toggleGps(true);
});
}
@Override
protected void enableLocation(boolean enabled) {
- if (enabled) {
- mapboxMap.setMyLocationEnabled(true);
- Location location = mapboxMap.getMyLocation();
- if (location != null) {
- onLocationChanged(location);
- } else {
- LocationSource.getLocationEngine(this).addLocationEngineListener(this);
- }
- } else {
- mapboxMap.setMyLocationEnabled(false);
- }
+ mapboxMap.setMyLocationEnabled(enabled);
}
@Override
@@ -86,9 +67,7 @@ public class MyLocationDrawableActivity extends BaseLocationActivity implements
@Override
public void onLocationChanged(Location location) {
- if (mapboxMap != null) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 14));
- }
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 14));
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
index a219b369f6..89774dc507 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
@@ -13,10 +13,8 @@ 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.location.LocationSource;
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;
@@ -38,80 +36,65 @@ public class MyLocationTintActivity extends BaseLocationActivity implements Loca
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
-
- // enable location updates
- toggleGps(!mapboxMap.isMyLocationEnabled());
-
- // add some padding
- final MyLocationViewSettings myLocationViewSettings = mapboxMap.getMyLocationViewSettings();
- myLocationViewSettings.setPadding(0, 500, 0, 0);
-
- // enable tracking
- TrackingSettings settings = mapboxMap.getTrackingSettings();
- settings.setDismissLocationTrackingOnGesture(false);
- settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
-
- // handle default button clicks
- ViewUtils.attachClickListener(
- MyLocationTintActivity.this,
- R.id.default_user_dot_coloring_button,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- myLocationViewSettings.setAccuracyTintColor(ContextCompat.getColor(
- MyLocationTintActivity.this, R.color.mapbox_blue));
- myLocationViewSettings.setForegroundTintColor(ContextCompat.getColor(
- MyLocationTintActivity.this, R.color.mapbox_blue));
- myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
- }
- });
-
- // handle tint user dot button clicks
- ViewUtils.attachClickListener(
- MyLocationTintActivity.this,
- R.id.tint_user_dot_button,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- myLocationViewSettings.setAccuracyTintColor(
- ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapboxGreen));
- myLocationViewSettings.setForegroundTintColor(
- ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapboxGreen));
- myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
- }
- });
-
- // handle tint accuracy ring button clicks
- ViewUtils.attachClickListener(
- MyLocationTintActivity.this,
- R.id.user_accuracy_ring_tint_button,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- myLocationViewSettings.setAccuracyTintColor(
- ContextCompat.getColor(MyLocationTintActivity.this, R.color.accent));
- myLocationViewSettings.setForegroundTintColor(
- ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapbox_blue));
- myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
- }
- });
-
- ViewUtils.attachClickListener(
- MyLocationTintActivity.this,
- R.id.user_dot_transparent_button,
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- myLocationViewSettings.setForegroundTintColor(Color.TRANSPARENT);
- myLocationViewSettings.setBackgroundTintColor(Color.TRANSPARENT);
- }
- }
- );
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+
+ // enable location updates
+ toggleGps(true);
+
+ // add some padding
+ final MyLocationViewSettings myLocationViewSettings = mapboxMap.getMyLocationViewSettings();
+ myLocationViewSettings.setPadding(0, 500, 0, 0);
+
+ // enable tracking
+ TrackingSettings settings = mapboxMap.getTrackingSettings();
+ settings.setDismissLocationTrackingOnGesture(false);
+ settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
+
+ // handle default button clicks
+ ViewUtils.attachClickListener(
+ MyLocationTintActivity.this,
+ R.id.default_user_dot_coloring_button,
+ view -> {
+ myLocationViewSettings.setAccuracyTintColor(ContextCompat.getColor(
+ MyLocationTintActivity.this, R.color.mapbox_blue));
+ myLocationViewSettings.setForegroundTintColor(ContextCompat.getColor(
+ MyLocationTintActivity.this, R.color.mapbox_blue));
+ myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
+ });
+
+ // handle tint user dot button clicks
+ ViewUtils.attachClickListener(
+ MyLocationTintActivity.this,
+ R.id.tint_user_dot_button,
+ view -> {
+ myLocationViewSettings.setAccuracyTintColor(
+ ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapboxGreen));
+ myLocationViewSettings.setForegroundTintColor(
+ ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapboxGreen));
+ myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
+ });
+
+ // handle tint accuracy ring button clicks
+ ViewUtils.attachClickListener(
+ MyLocationTintActivity.this,
+ R.id.user_accuracy_ring_tint_button,
+ view -> {
+ myLocationViewSettings.setAccuracyTintColor(
+ ContextCompat.getColor(MyLocationTintActivity.this, R.color.accent));
+ myLocationViewSettings.setForegroundTintColor(
+ ContextCompat.getColor(MyLocationTintActivity.this, R.color.mapbox_blue));
+ myLocationViewSettings.setBackgroundTintColor(Color.WHITE);
+ });
+
+ ViewUtils.attachClickListener(
+ MyLocationTintActivity.this,
+ R.id.user_dot_transparent_button,
+ view -> {
+ myLocationViewSettings.setForegroundTintColor(Color.TRANSPARENT);
+ myLocationViewSettings.setBackgroundTintColor(Color.TRANSPARENT);
+ }
+ );
});
}
@@ -133,7 +116,6 @@ public class MyLocationTintActivity extends BaseLocationActivity implements Loca
protected void onStart() {
super.onStart();
mapView.onStart();
- LocationSource.getLocationEngine(this).addLocationEngineListener(this);
}
@Override
@@ -151,7 +133,6 @@ public class MyLocationTintActivity extends BaseLocationActivity implements Loca
@Override
protected void onStop() {
super.onStop();
- LocationSource.getLocationEngine(this).removeLocationEngineListener(this);
mapView.onStop();
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
index ac6c346a88..ea3a6f14bc 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
@@ -1,57 +1,52 @@
package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-import android.location.Location;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
-import android.view.View;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.location.LocationSource;
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.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
+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;
- private LocationEngine locationServices;
- private LocationEngineListener locationListener;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_location_toggle);
- locationServices = LocationSource.getLocationEngine(this);
-
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- }
- });
+ mapView.getMapAsync(map -> mapboxMap = map);
locationToggleFab = (FloatingActionButton) findViewById(R.id.fabLocationToggle);
- locationToggleFab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- toggleGps(!mapboxMap.isMyLocationEnabled());
- }
+ locationToggleFab.setOnClickListener(view -> {
+ if (mapboxMap != null) {
+ toggleGps(!mapboxMap.isMyLocationEnabled());
}
});
}
@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();
@@ -85,11 +80,6 @@ public class MyLocationToggleActivity extends BaseLocationActivity {
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
- // Ensure no memory leak occurs if we register the location listener but the call hasn't
- // been made yet.
- if (locationListener != null) {
- locationServices.removeLocationEngineListener(locationListener);
- }
}
@Override
@@ -98,40 +88,4 @@ public class MyLocationToggleActivity extends BaseLocationActivity {
mapView.onLowMemory();
}
- @Override
- protected void enableLocation(boolean enabled) {
- if (enabled) {
- // To move the camera instantly, we attempt to get the last known location and either
- // ease or animate the camera to that position depending on the zoom level.
- Location lastLocation = LocationSource.getLocationEngine(this).getLastLocation();
-
- if (lastLocation != null) {
- if (mapboxMap.getCameraPosition().zoom > 15.99) {
- mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(new LatLng(lastLocation)), 1000);
- } else {
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lastLocation), 16), 1000);
- }
- } else {
- locationListener = new LocationEngineListener() {
- @Override
- public void onConnected() {
- // Nothing
- }
-
- @Override
- public void onLocationChanged(Location location) {
- if (location != null) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 16));
- locationServices.removeLocationEngineListener(this);
- }
- }
- };
- locationServices.addLocationEngineListener(locationListener);
- }
- locationToggleFab.setImageResource(R.drawable.ic_location_disabled);
- } else {
- locationToggleFab.setImageResource(R.drawable.ic_my_location);
- }
- mapboxMap.setMyLocationEnabled(enabled);
- }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
index 3a3301b87f..5ebe43e68c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
@@ -1,5 +1,6 @@
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;
@@ -13,14 +14,20 @@ 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.
@@ -29,7 +36,8 @@ import com.mapbox.mapboxsdk.testapp.R;
* using gesture configurations.
* </p>
*/
-public class MyLocationTrackingModeActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
+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;
@@ -41,6 +49,7 @@ public class MyLocationTrackingModeActivity extends AppCompatActivity implements
private MapboxMap mapboxMap;
private Spinner locationSpinner;
private Spinner bearingSpinner;
+ private boolean firstRun = true;
private MenuItem dismissLocationTrackingOnGestureItem;
private MenuItem dismissBearingTrackingOnGestureItem;
@@ -51,7 +60,43 @@ public class MyLocationTrackingModeActivity extends AppCompatActivity implements
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);
@@ -60,73 +105,55 @@ public class MyLocationTrackingModeActivity extends AppCompatActivity implements
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);
}
+ }
- 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);
+ private void setupSpinners(@NonNull MapboxMap mapboxMap) {
+ locationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+ bearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+ setCheckBoxes();
- 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);
+ mapboxMap.setOnMyLocationTrackingModeChangeListener(myLocationTrackingMode -> {
+ locationSpinner.setOnItemSelectedListener(null);
+ switch (myLocationTrackingMode) {
+ case MyLocationTracking.TRACKING_NONE:
+ locationSpinner.setSelection(TRACKING_NONE_INDEX);
+ break;
+ case MyLocationTracking.TRACKING_FOLLOW:
+ locationSpinner.setSelection(TRACKING_FOLLOW_INDEX);
+ break;
+ }
+ locationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
+ });
- mapView = (MapView) findViewById(R.id.mapView);
- mapView.onCreate(savedInstanceState);
+ mapboxMap.setOnMyBearingTrackingModeChangeListener(myBearingTrackingMode -> {
+ bearingSpinner.setOnItemSelectedListener(null);
+ switch (myBearingTrackingMode) {
+ case MyBearingTracking.NONE:
+ bearingSpinner.setSelection(BEARING_NONE_INDEX);
+ break;
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(@NonNull MapboxMap mapboxMap) {
- MyLocationTrackingModeActivity.this.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);
- }
- });
-
- if (savedInstanceState == null) {
- mapboxMap.setMyLocationEnabled(true);
- }
+ case MyBearingTracking.GPS:
+ bearingSpinner.setSelection(BEARING_GPS_INDEX);
+ break;
+
+ case MyBearingTracking.COMPASS:
+ bearingSpinner.setSelection(BEARING_COMPASS_INDEX);
+ break;
}
+ bearingSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
});
}
@@ -186,6 +213,8 @@ public class MyLocationTrackingModeActivity extends AppCompatActivity implements
@Override
protected void onStop() {
super.onStop();
+ Mapbox.getLocationEngine().removeLocationEngineListener(this);
+ Mapbox.getLocationEngine().removeLocationUpdates();
mapView.onStop();
}
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/annotations/PulseMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java
index d9c6357774..d752e5d0ef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java
@@ -66,14 +66,14 @@ public class PulseMarkerViewOptions extends BaseMarkerViewOptions<PulseMarkerVie
return new PulseMarkerView(this);
}
- public static final Parcelable.Creator<CountryMarkerViewOptions> CREATOR
- = new Parcelable.Creator<CountryMarkerViewOptions>() {
- public CountryMarkerViewOptions createFromParcel(Parcel in) {
- return new CountryMarkerViewOptions(in);
+ public static final Parcelable.Creator<PulseMarkerViewOptions> CREATOR
+ = new Parcelable.Creator<PulseMarkerViewOptions>() {
+ public PulseMarkerViewOptions createFromParcel(Parcel in) {
+ return new PulseMarkerViewOptions(in);
}
- public CountryMarkerViewOptions[] newArray(int size) {
- return new CountryMarkerViewOptions[size];
+ public PulseMarkerViewOptions[] newArray(int size) {
+ return new PulseMarkerViewOptions[size];
}
};
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java
index 8c049d7730..aaad2f04ab 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java
@@ -11,5 +11,6 @@ public class ExampleCustomLayer {
public static long InitializeFunction;
public static long RenderFunction;
+ public static long ContextLostFunction;
public static long DeinitializeFunction;
}
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 f717daeada..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: " + 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..97f70e33bb 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java
@@ -2,12 +2,11 @@ package com.mapbox.mapboxsdk.testapp.utils;
import android.content.Context;
import android.text.TextUtils;
-
import com.mapbox.mapboxsdk.geometry.LatLng;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.FeatureCollection;
+import com.mapbox.services.commons.geojson.Point;
+import com.mapbox.services.commons.models.Position;
import java.io.BufferedReader;
import java.io.IOException;
@@ -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.getFeatures()) {
+ if (feature.getGeometry() instanceof Point) {
+ Position point = ((Point) feature.getGeometry()).getCoordinates();
+ latLngs.add(new LatLng(point.getLatitude(), point.getLongitude()));
}
}
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 8c6ab3e211..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("Failed to encode metadata: " + exception.getMessage());
+ 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
new file mode 100644
index 0000000000..f0cca57e10
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java
@@ -0,0 +1,36 @@
+package com.mapbox.mapboxsdk.testapp.utils;
+
+import android.content.Context;
+import android.support.annotation.RawRes;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+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 {
+ 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;
+ }
+}
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
index 6f20d6fb0f..235fd8233b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java
@@ -146,15 +146,15 @@ public class TimingLogger {
if (disabled) {
return;
}
- Timber.d(label + ": begin");
+ Timber.d("%s: begin", label);
final long first = splits.get(0);
long now = first;
for (int i = 1; i < splits.size(); i++) {
now = splits.get(i);
final String splitLabel = splitLabels.get(i);
final long prev = splits.get(i - 1);
- Timber.d(label + ": " + (now - prev) + " ms, " + splitLabel);
+ Timber.d("%s: %s ms, %s", label, (now - prev), splitLabel);
}
- Timber.d(label + ": end, " + (now - first) + " ms");
+ Timber.d("%s: end, %s ms", label, (now - first));
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java
new file mode 100644
index 0000000000..a69fb48ab4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java
@@ -0,0 +1,74 @@
+package com.mapbox.mapboxsdk.testapp.view;
+
+import android.content.Context;
+import android.support.design.widget.BottomSheetBehavior;
+import android.support.design.widget.CoordinatorLayout;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class LockableBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
+
+ private boolean locked = false;
+
+ public LockableBottomSheetBehavior(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+ boolean handled = false;
+ if (!locked) {
+ handled = super.onInterceptTouchEvent(parent, child, event);
+ }
+ return handled;
+ }
+
+ @Override
+ public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+ boolean handled = false;
+ if (!locked) {
+ handled = super.onTouchEvent(parent, child, event);
+ }
+ return handled;
+ }
+
+ @Override
+ public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target,
+ int nestedScrollAxes) {
+ boolean handled = false;
+ if (!locked) {
+ handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
+ }
+ return handled;
+ }
+
+ @Override
+ public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy,
+ int[] consumed) {
+ if (!locked) {
+ super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
+ }
+ }
+
+ @Override
+ public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
+ if (!locked) {
+ super.onStopNestedScroll(coordinatorLayout, child, target);
+ }
+ }
+
+ @Override
+ public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX,
+ float velocityY) {
+ boolean handled = false;
+ if (!locked) {
+ handled = super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
+ }
+ return handled;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/MapViewPager.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/MapViewPager.java
new file mode 100644
index 0000000000..92c28d7ed4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/MapViewPager.java
@@ -0,0 +1,20 @@
+package com.mapbox.mapboxsdk.testapp.view;
+
+import android.content.Context;
+import android.support.v4.view.PagerTabStrip;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.SurfaceView;
+import android.view.View;
+
+public class MapViewPager extends ViewPager {
+
+ public MapViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
+ return v instanceof SurfaceView || v instanceof PagerTabStrip || (super.canScroll(v, checkV, dx, x, y));
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_white.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_white.xml
new file mode 100644
index 0000000000..cd5c045783
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml
new file mode 100644
index 0000000000..ded53fc4f2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml
@@ -0,0 +1,9 @@
+<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:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"
+ android:fillColor="#F1F1F1"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_check_box.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_check_box.xml
new file mode 100644
index 0000000000..cf8bfa24b5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_check_box.xml
@@ -0,0 +1,9 @@
+<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="#FF000000"
+ android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml
deleted file mode 100644
index 3217661b64..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
-
- <solid
- android:color="@color/mapbox_blue"/>
-
- <size
- android:width="@dimen/circle_size"
- android:height="@dimen/circle_size"/>
-</shape>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml
new file mode 100644
index 0000000000..bf9b895aca
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml
@@ -0,0 +1,9 @@
+<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="#FF000000"
+ android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml
deleted file mode 100644
index 71992ebd7f..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <size android:width="10px" android:height="10px"/>
- <solid android:color="@color/redAccent"/>
-</shape> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml
index ac1f08e821..26b40b9ab6 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml
@@ -5,25 +5,13 @@
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="46.437"
- app:mapbox_cameraTargetLng="-80.425"
- app:mapbox_cameraZoom="3"
- app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
-
-<android.support.design.widget.FloatingActionButton
- android:id="@+id/fabStartStop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_marginBottom="@dimen/fab_margin"
- android:layout_marginRight="@dimen/fab_margin"
- android:src="@android:drawable/ic_media_play"
- app:backgroundTint="@color/primary"/>
+ <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_bottom_sheet.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_bottom_sheet.xml
new file mode 100644
index 0000000000..cbb440d926
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_bottom_sheet.xml
@@ -0,0 +1,76 @@
+<?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"
+ android:fitsSystemWindows="true"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+
+ <android.support.v4.widget.NestedScrollView
+ android:id="@+id/bottom_sheet"
+ android:layout_width="match_parent"
+ android:layout_height="250dp"
+ android:background="@color/primaryDark"
+ app:behavior_hideable="true"
+ app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:textColor="@color/white"
+ android:text="^"
+ android:textSize="22sp"
+ tools:ignore="HardcodedText"/>
+
+ <FrameLayout
+ android:id="@+id/fragment_container_bottom"
+ android:layout_width="match_parent"
+ android:layout_height="186dp"/>
+
+ </LinearLayout>
+
+ </android.support.v4.widget.NestedScrollView>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabBottomSheet"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/fab_margin"
+ app:backgroundTint="@color/primary"
+ android:src="@drawable/ic_refresh"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="top|end"/>
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:clipToPadding="false"
+ android:paddingBottom="8dp"
+ app:layout_anchor="@id/fabBottomSheet"
+ app:layout_anchorGravity="top">
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabFragment"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_add_white"
+ app:backgroundTint="@color/accent"/>
+ </FrameLayout>
+</android.support.design.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml
index fa37c485d7..c8752df1bf 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout 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">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
@@ -20,8 +18,8 @@
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_marginBottom="82dp"
- android:layout_marginRight="@dimen/fab_margin"
android:layout_marginEnd="@dimen/fab_margin"
+ android:layout_marginRight="@dimen/fab_margin"
android:src="@drawable/ic_my_location"
app:backgroundTint="@color/accent"
app:layout_anchorGravity="top"/>
@@ -35,4 +33,4 @@
android:src="@drawable/ic_paint"
app:backgroundTint="@color/primary"/>
-</FrameLayout>
+</merge>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml
index b70bb6d7b2..0cb065a676 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml
@@ -15,7 +15,9 @@
app:mapbox_cameraZoom="15"/>
<LinearLayout
+ style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
+ android:background="@color/primary"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
@@ -23,6 +25,7 @@
<Button
android:id="@+id/cameraMoveButton"
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -30,6 +33,7 @@
<Button
android:id="@+id/cameraEaseButton"
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -37,6 +41,7 @@
<Button
android:id="@+id/cameraAnimateButton"
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml
new file mode 100644
index 0000000000..cb14aab91f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml
@@ -0,0 +1,27 @@
+<?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">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="37.787947"
+ app:mapbox_cameraTargetLng="-122.407432"
+ app:mapbox_cameraZoom="11"
+ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ 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_play_arrow_black_24dp"
+ app:backgroundTint="@android:color/white"/>
+
+</android.support.design.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml
index 6e8a4e5eb2..5ac55e75e2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml
@@ -23,6 +23,7 @@
android:layout_alignParentRight="true"
android:layout_marginBottom="@dimen/fab_margin"
android:layout_marginRight="@dimen/fab_margin"
+ android:layout_marginEnd="@dimen/fab_margin"
android:src="@drawable/ic_directions_bus_black"
app:backgroundTint="@android:color/white"/>
@@ -36,6 +37,7 @@
android:layout_marginBottom="@dimen/fab_margin"
android:layout_marginRight="@dimen/fab_margin"
android:src="@drawable/ic_layers"
+ android:layout_marginEnd="@dimen/fab_margin"
app:backgroundTint="@color/primary"/>
</RelativeLayout>
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_debug_mode.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml
index 6db8b073d9..c6f3c0e3f2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml
@@ -1,46 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout
+<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/coordinator_layout"
+ android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:clickable="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true">
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
+ <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"
- app:mapbox_uiAttribution="false"
- app:mapbox_uiCompass="false"
- app:mapbox_uiLogo="false"/>
+ android:orientation="vertical">
- <TextView
- android:id="@+id/textZoom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|start"
- android:layout_margin="8dp"
- android:textSize="14sp"/>
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_uiAttribution="false"
+ app:mapbox_uiCompass="false"
+ app:mapbox_uiLogo="false"/>
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/fabDebug"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|bottom"
- android:layout_marginBottom="82dp"
- android:layout_marginRight="@dimen/fab_margin"
- android:src="@drawable/ic_refresh"
- app:backgroundTint="@color/accent"
- app:layout_anchorGravity="top"/>
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/fabStyles"
+ <TextView
+ android:id="@+id/textZoom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|start"
+ android:layout_margin="8dp"
+ android:textIsSelectable="true"
+ android:textSize="14sp"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="bottom|start"/>
+
+ <TextView
+ android:id="@+id/fpsView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:layout_margin="8dp"
+ android:textIsSelectable="true"
+ android:textSize="14sp"
+ app:layout_anchor="@id/textZoom"
+ app:layout_anchorGravity="top"/>
+
+ <android.support.v4.widget.NestedScrollView
+ android:id="@+id/bottom_sheet"
+ android:layout_width="match_parent"
+ android:layout_height="250dp"
+ android:background="@color/white"
+ android:visibility="invisible"
+ app:behavior_hideable="true"
+ app:layout_behavior="android.support.design.widget.BottomSheetBehavior"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabDebug"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:layout_marginBottom="82dp"
+ android:layout_marginEnd="@dimen/fab_margin"
+ android:layout_marginRight="@dimen/fab_margin"
+ android:src="@drawable/ic_refresh"
+ app:backgroundTint="@color/accent"
+ app:layout_anchor="@+id/fabStyles"
+ app:layout_anchorGravity="top"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@id/fabStyles"
+ 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_layers"
+ app:backgroundTint="@color/primary"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="bottom|end"/>
+
+ </android.support.design.widget.CoordinatorLayout>
+
+ <android.support.design.widget.NavigationView
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_layers"
- app:backgroundTint="@color/primary"/>
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:background="@android:color/white">
+
+ <ListView
+ android:id="@+id/listView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </android.support.design.widget.NavigationView>
-</android.support.design.widget.CoordinatorLayout>
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
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_latlngbounds.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml
new file mode 100644
index 0000000000..e565c3c9d1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml
@@ -0,0 +1,36 @@
+<?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:fitsSystemWindows="true"
+ android:orientation="vertical">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
+
+ <android.support.v4.widget.NestedScrollView
+ android:id="@+id/bottom_sheet"
+ android:layout_width="match_parent"
+ android:layout_height="375dp"
+ android:background="@color/primaryDark"
+ app:behavior_hideable="true"
+ app:behavior_skipCollapsed="false"
+ app:layout_behavior="com.mapbox.mapboxsdk.testapp.view.LockableBottomSheetBehavior"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_arrow_downward"
+ app:backgroundTint="@color/primary"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="top|end"/>
+
+</android.support.design.widget.CoordinatorLayout> \ 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
new file mode 100644
index 0000000000..856dd24752
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".activity.maplayout.LocalGlyphActivity">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_localIdeographFontFamily="Droid Sans" />
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
index 43fa8fb995..419660b36a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml
@@ -1,17 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/fragment_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context=".activity.fragment.MapFragmentActivity">
-
- <FrameLayout
- android:id="@+id/fragment_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
-</LinearLayout>
+ android:layout_height="match_parent"/>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml
index 3fd66977e2..0a12fd9e50 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml
@@ -1,8 +1,5 @@
<?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="match_parent">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/button_open_dialog"
@@ -11,4 +8,4 @@
android:layout_gravity="center"
android:text="@string/button_open_dialog"/>
-</FrameLayout>
+</merge>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
index a0de31ee48..cd4aa4bdef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml
@@ -1,19 +1,16 @@
<?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="match_parent"
- android:orientation="vertical">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"/>
<View
android:layout_width="@dimen/map_padding_left"
android:layout_height="match_parent"
android:alpha="0.5"
- android:background="@color/mapbox_blue" />
+ android:background="@color/mapbox_blue"/>
<View
android:layout_width="match_parent"
@@ -24,13 +21,13 @@
android:layout_marginRight="@dimen/map_padding_right"
android:layout_marginStart="@dimen/map_padding_left"
android:alpha="0.5"
- android:background="@color/mapbox_blue" />
+ android:background="@color/mapbox_blue"/>
<View
android:layout_width="@dimen/map_padding_right"
android:layout_height="match_parent"
android:layout_gravity="end"
android:alpha="0.5"
- android:background="@color/mapbox_blue" />
+ android:background="@color/mapbox_blue"/>
-</FrameLayout>
+</merge>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml
new file mode 100644
index 0000000000..30ad494dca
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <GridLayout
+ android:id="@+id/snapshot_grid"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:columnCount="3"
+ android:orientation="horizontal"
+ android:rowCount="3"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_marker.xml
new file mode 100644
index 0000000000..a10fb904db
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_marker.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/snapshot_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:contentDescription=""/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_reuse.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_reuse.xml
new file mode 100644
index 0000000000..5ce25a69d0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_reuse.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/snapshot_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:contentDescription=""/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fab"
+ 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="end|bottom"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_animate_coordinates"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_visibility.xml
index 6da5a61ecb..c33034181c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_visibility.xml
@@ -1,13 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/viewParent"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:orientation="vertical"
+ tools:context=".activity.maplayout.SimpleMapActivity">
<com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
+ android:id="@id/mapView"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="match_parent"
+ android:visibility="invisible"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
index ff28d2edf0..52691a26fe 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml
@@ -17,6 +17,7 @@
<TextView
android:id="@+id/countView"
+ android:textIsSelectable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/toolbar"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml
index cf4b51bbe0..dae3a1d9b7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml
@@ -10,16 +10,18 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
+ android:textIsSelectable="false"
app:mapbox_cameraTargetLat="38.907192"
app:mapbox_cameraTargetLng="-77.036871"
- app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"
- app:mapbox_cameraZoom="12" />
+ app:mapbox_cameraZoom="12"
+ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
<TextView
android:id="@+id/countView"
+ android:textIsSelectable="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
- android:textSize="20sp" />
+ android:textSize="20sp"/>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml
index 084675fb2c..1eb999caf5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml
@@ -15,7 +15,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
- android:text="No Results"
+ android:text="@string/no_results"
android:textSize="24sp"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml
index 599ae3fa1c..193ae55e59 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml
@@ -2,13 +2,13 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/tools"
- android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:orientation="vertical"
+ mapbox:ignore="NestedWeights">
<LinearLayout
- android:id="@+id/map_container1"
+ android:baselineAligned="false"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
@@ -40,7 +40,7 @@
</LinearLayout>
<LinearLayout
- android:id="@+id/map_container2"
+ android:baselineAligned="false"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
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
index d65d5796f1..addfe8427b 100644
--- 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
@@ -1,22 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <FrameLayout
+ <android.support.v4.widget.ContentLoadingProgressBar
android:id="@id/progress"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
- <android.support.v4.widget.ContentLoadingProgressBar
- style="?android:attr/progressBarStyleLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"/>
-
- </FrameLayout>
-
-</LinearLayout>
+</merge>
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
index 95f084506b..7236a944e9 100644
--- 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
@@ -45,6 +45,6 @@
app:mapbox_myLocationTintColor="@color/primary"
app:mapbox_myLocationAccuracyTintColor="@color/primary"
app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"
- app:mapbox_cameraZoom="15" />
+ app:mapbox_cameraZoom="8" />
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml
deleted file mode 100644
index 26a71bc568..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/drawer_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.mapbox.mapboxsdk.testapp.activity.maplayout.NavigationDrawerActivity">
-
- <FrameLayout
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:fitsSystemWindows="true">
-
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="56dp" />
-
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/navigation_drawer"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="start"
- android:fitsSystemWindows="true" />
-
-</android.support.v4.widget.DrawerLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml
index d4b64b1ea2..3e21015e96 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml
@@ -18,27 +18,30 @@
android:layout_height="match_parent"
android:layout_below="@+id/progress_bar"/>
- <Button
- android:id="@+id/button_download_region"
- android:layout_width="wrap_content"
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_margin="@dimen/fab_margin"
- android:background="@color/white"
- android:padding="10dp"
- android:text="@string/button_download_region"/>
+ android:background="@color/primary"
+ android:orientation="horizontal"
+ android:weightSum="2">
- <Button
- android:id="@+id/button_list_regions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBottom="@+id/button_download_region"
- android:layout_alignParentRight="true"
- android:layout_marginRight="@dimen/fab_margin"
- android:background="@color/white"
- android:padding="10dp"
- android:text="@string/button_list_regions"/>
+ <Button
+ android:id="@+id/button_download_region"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_download_region"/>
+
+ <Button
+ android:id="@+id/button_list_regions"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/button_list_regions"/>
+ </LinearLayout>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml
index 084675fb2c..1eb999caf5 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml
@@ -15,7 +15,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
- android:text="No Results"
+ android:text="@string/no_results"
android:textSize="24sp"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
index bc24533960..cfbd07ce39 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml
@@ -1,7 +1,8 @@
<?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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
@@ -22,7 +23,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:paddingBottom="8dp"
- android:text="Move the map by x/y pixels"
+ android:text="@string/action_scroll_by"
android:textColor="#FFFFFF"
android:textSize="20sp" />
@@ -32,7 +33,8 @@
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/seekbar_move_x"
android:layout_below="@id/title"
- android:text="X: 0000" />
+ android:text="X: 0000"
+ tools:ignore="HardcodedText"/>
<SeekBar
android:id="@id/seekbar_move_x"
@@ -40,6 +42,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_marginLeft="56dp"
+ android:layout_marginStart="56dp"
android:max="50"
android:progress="0" />
@@ -48,7 +51,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/seekbar_move_y"
- android:text="Y: 0000" />
+ android:text="Y: 0000"
+ tools:ignore="HardcodedText"/>
<SeekBar
android:id="@id/seekbar_move_y"
@@ -58,6 +62,7 @@
android:layout_marginBottom="8dp"
android:layout_marginLeft="56dp"
android:layout_marginTop="16dp"
+ android:layout_marginStart="56dp"
android:max="50"
android:progress="0" />
@@ -66,7 +71,6 @@
</android.support.v7.widget.Toolbar>
<FrameLayout
- android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar">
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
index 6b99711e84..53345571b4 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml
@@ -16,6 +16,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
+ android:contentDescription="@null"
android:background="@color/primary"/>
<com.mapbox.mapboxsdk.maps.MapView
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml
index b133f3d9a5..83150be4bf 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml
@@ -15,7 +15,18 @@
app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/>
<android.support.design.widget.FloatingActionButton
- android:id="@id/fab"
+ android:id="@+id/fab_style_json"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_above="@+id/fab_file"
+ android:layout_margin="@dimen/fab_margin"
+ android:src="@drawable/ic_add"
+ app:backgroundTint="@android:color/white"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@id/fab_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml
index 89a28a7799..ffcdddce57 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<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">
@@ -8,6 +9,7 @@
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
+ android:layout_height="match_parent"
+ app:mapbox_styleUrl="@string/mapbox_style_outdoors"/>
-</LinearLayout>
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml
new file mode 100644
index 0000000000..de0ef0ef36
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml
@@ -0,0 +1,29 @@
+<?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:background="@color/mapbox_blue"
+ android:orientation="vertical">
+
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_margin="50dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_renderTextureMode="true"/>
+
+ <TextView
+ android:id="@+id/fpsView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:layout_margin="16dp"
+ android:textIsSelectable="true"
+ android:textSize="14sp"/>
+
+</android.support.design.widget.CoordinatorLayout>
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml
new file mode 100644
index 0000000000..480de550ad
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.DrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clickable="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true">
+
+ <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">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_renderTextureMode="true"
+ app:mapbox_uiAttribution="false"
+ app:mapbox_uiCompass="false"
+ app:mapbox_uiLogo="false"/>
+
+ <TextView
+ android:id="@+id/textZoom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|start"
+ android:layout_margin="8dp"
+ android:textIsSelectable="true"
+ android:textSize="14sp"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="bottom|start"/>
+
+ <TextView
+ android:id="@+id/fpsView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:layout_margin="8dp"
+ android:textIsSelectable="true"
+ android:textSize="14sp"
+ app:layout_anchor="@id/textZoom"
+ app:layout_anchorGravity="top"/>
+
+ <android.support.v4.widget.NestedScrollView
+ android:id="@+id/bottom_sheet"
+ android:layout_width="match_parent"
+ android:layout_height="250dp"
+ android:background="@color/white"
+ android:visibility="invisible"
+ app:behavior_hideable="true"
+ app:layout_behavior="android.support.design.widget.BottomSheetBehavior"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabDebug"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:layout_marginBottom="82dp"
+ android:layout_marginEnd="@dimen/fab_margin"
+ android:layout_marginRight="@dimen/fab_margin"
+ android:src="@drawable/ic_refresh"
+ app:backgroundTint="@color/accent"
+ app:layout_anchor="@+id/fabStyles"
+ app:layout_anchorGravity="top"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@id/fabStyles"
+ 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_layers"
+ app:backgroundTint="@color/primary"
+ app:layout_anchor="@id/bottom_sheet"
+ app:layout_anchorGravity="bottom|end"/>
+
+ </android.support.design.widget.CoordinatorLayout>
+
+ <android.support.design.widget.NavigationView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:background="@android:color/white">
+
+ <ListView
+ android:id="@+id/listView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </android.support.design.widget.NavigationView>
+
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
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
new file mode 100644
index 0000000000..2baa3d39dd
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml
@@ -0,0 +1,29 @@
+<?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">
+
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_renderTextureMode="true"/>
+
+ <android.support.design.widget.FloatingActionButton
+ android:id="@+id/fabResize"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:layout_marginBottom="@dimen/fab_margin"
+ android:layout_marginRight="@dimen/fab_margin"
+ android:src="@drawable/ic_refresh"
+ app:backgroundTint="@color/accent"
+ app:layout_anchorGravity="bottom"/>
+
+</android.support.design.widget.CoordinatorLayout>
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml
index d931cb3643..3edaff6985 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml
@@ -4,19 +4,18 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <android.support.v4.view.ViewPager
+ <com.mapbox.mapboxsdk.testapp.view.MapViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.PagerTabStrip
- android:id="@+id/viewpager_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingBottom="4dp"
android:paddingTop="4dp"/>
- </android.support.v4.view.ViewPager>
+ </com.mapbox.mapboxsdk.testapp.view.MapViewPager>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml
new file mode 100644
index 0000000000..0bb59451ab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_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="40.730648"
+ app:mapbox_cameraTargetLng="-73.993619"
+ app:mapbox_cameraZoom="11"
+ app:mapbox_styleUrl="@string/mapbox_style_light"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml
index 1c9fbbd482..a7f422f9ce 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml
@@ -16,7 +16,7 @@
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Latitude"
+ android:text="@string/latitude"
android:textColor="@android:color/white" />
<SeekBar
@@ -25,7 +25,9 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_lat"
android:layout_toLeftOf="@+id/value_lat"
+ android:layout_toEndOf="@+id/text_lat"
android:layout_toRightOf="@id/text_lat"
android:max="360" />
@@ -38,7 +40,7 @@
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="-180"
+ android:text="@string/min_value"
android:textColor="@android:color/white" />
</RelativeLayout>
@@ -56,7 +58,7 @@
android:textColor="@android:color/white"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Longitude" />
+ android:text="@string/longitude" />
<SeekBar
android:id="@+id/seekbar_lon"
@@ -64,8 +66,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_lon"
android:layout_toLeftOf="@+id/value_lon"
android:layout_toRightOf="@id/text_lon"
+ android:layout_toEndOf="@id/text_lon"
android:max="360" />
<TextView
@@ -78,7 +82,7 @@
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="-180" />
+ android:text="@string/min_value" />
</RelativeLayout>
@@ -95,7 +99,7 @@
android:textColor="@android:color/white"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Zoom" />
+ android:text="@string/zoom" />
<SeekBar
android:id="@+id/seekbar_zoom"
@@ -103,8 +107,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_zoom"
android:layout_toLeftOf="@+id/value_zoom"
android:layout_toRightOf="@id/text_zoom"
+ android:layout_toEndOf="@+id/text_zoom"
android:max="18" />
<TextView
@@ -117,7 +123,7 @@
android:textColor="@android:color/white"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="18" />
+ android:text="@string/default_zoom_value" />
</RelativeLayout>
@@ -134,7 +140,7 @@
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Bearing" />
+ android:text="@string/bearing" />
<SeekBar
android:id="@+id/seekbar_bearing"
@@ -142,8 +148,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_bearing"
android:layout_toLeftOf="@+id/value_bearing"
android:layout_toRightOf="@id/text_bearing"
+ android:layout_toEndOf="@id/text_bearing"
android:max="360" />
<TextView
@@ -156,7 +164,7 @@
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="0" />
+ android:text="@string/default_tilt_value" />
</RelativeLayout>
@@ -173,7 +181,7 @@
android:textColor="@android:color/white"
android:layout_marginRight="4dp"
android:gravity="center"
- android:text="Tilt" />
+ android:text="@string/tilt" />
<SeekBar
android:id="@+id/seekbar_tilt"
@@ -181,8 +189,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
+ android:layout_toStartOf="@+id/value_tilt"
android:layout_toLeftOf="@+id/value_tilt"
android:layout_toRightOf="@id/text_tilt"
+ android:layout_toEndOf="@id/text_tilt"
android:max="60" />
<TextView
@@ -195,7 +205,7 @@
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:gravity="center"
- android:text="0" />
+ android:text="@string/default_tilt_value" />
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml
index afebfa1c47..b8ea3d847e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml
@@ -12,7 +12,6 @@
mapbox:mapbox_cameraTargetLat="47.6077"
mapbox:mapbox_cameraTargetLng="-122.3421"
mapbox:mapbox_cameraZoom="11"
- mapbox:mapbox_renderTextureMode="true"
mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v10" />
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
index b976013ead..3cf2fbea55 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ xmlns:maps="http://schemas.android.com/apk/res-auto">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@id/mapView"
@@ -9,7 +10,6 @@
android:layout_height="match_parent" />
<FrameLayout
- android:id="@+id/map_card"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="5dp"
@@ -19,6 +19,7 @@
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mini_map"
android:layout_width="100dp"
+ maps:mapbox_enableZMediaOverlay="true"
android:layout_height="100dp" />
</FrameLayout>
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml
index b290d013f0..49b38f081a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml
@@ -20,6 +20,7 @@
android:gravity="center_vertical"
android:maxLines="1"
android:textColor="@android:color/black"
+ android:textIsSelectable="false"
android:textSize="16sp"/>
<TextView
@@ -31,6 +32,7 @@
android:alpha="0.56"
android:maxLines="1"
android:textColor="@android:color/black"
+ android:textIsSelectable="false"
android:textSize="14sp"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml
index 75f6ac9588..afec1f3bea 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml
@@ -18,6 +18,7 @@
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
+ android:textIsSelectable="false"
android:layout_marginTop="16dp"
android:alpha="0.54"
android:background="@android:color/transparent"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml
index 324a4861c3..cafa7d9746 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml
@@ -6,6 +6,7 @@
<ImageView
android:id="@id/imageView"
android:layout_width="64dp"
+ android:contentDescription="@null"
android:layout_height="64dp"/>
<TextView
@@ -14,6 +15,7 @@
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="2dp"
+ android:textIsSelectable="false"
android:textColor="@android:color/white"
android:textStyle="bold"/>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml
index 299865be9e..1ef2c69012 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml
@@ -11,6 +11,7 @@
android:layout_height="wrap_content"
android:textStyle="bold"
android:padding="4dp"
+ android:textIsSelectable="false"
android:layout_centerInParent="true" />
</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_animator.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_animator.xml
new file mode 100644
index 0000000000..db5a62d2cb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_animator.xml
@@ -0,0 +1,20 @@
+<?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">
+ <item
+ android:id="@+id/menu_action_accelerate_decelerate_interpolator"
+ android:title="@string/menuitem_title_accelerate_decelerate"
+ app:showAsAction="never"/>
+ <item
+ android:id="@+id/menu_action_bounce_interpolator"
+ android:title="@string/menuitem_title_bounce"
+ app:showAsAction="never"/>
+ <item
+ android:id="@+id/menu_action_anticipate_overshoot_interpolator"
+ android:title="@string/menuitem_title_anticipate_overshoot"
+ app:showAsAction="never"/>
+ <item
+ android:id="@+id/menu_action_path_interpolator"
+ android:title="@string/menuitem_title_path"
+ app:showAsAction="never"/>
+</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml
index 92d1dd5380..ff65f319f9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml
@@ -3,10 +3,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_action_intensity"
- android:title="Change intensity"
+ android:title="@string/change_intensity"
app:showAsAction="never"/>
<item
android:id="@+id/menu_action_anchor"
- android:title="Change Anchor"
+ android:title="@string/change_anchor"
app:showAsAction="never"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml
index 8b7245c5ca..43a191f7b1 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml
@@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/spinner"
- android:title="Amount of markers"
+ android:title="@string/amount_of_markers"
app:actionViewClass="android.widget.Spinner"
app:showAsAction="always"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml
index 4639dd65ba..915afd77fa 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml
@@ -1,17 +1,15 @@
<?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">
-
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_update_layer"
- android:title="Update layer (invalidate)" />
+ android:title="@string/update_layer_invalidate"/>
<item
android:id="@+id/action_set_color_red"
- android:title="Red" />
+ android:title="@string/red"/>
<item
android:id="@+id/action_set_color_green"
- android:title="Green" />
+ android:title="@string/green"/>
<item
android:id="@+id/action_set_color_blue"
- android:title="Blue" />
+ android:title="@string/blue"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml
index 3eae56a273..a596ff5708 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml
@@ -4,47 +4,47 @@
<item
android:id="@+id/action_add_exponential_zoom_function"
- android:title="Add an exponential zoom function"
+ android:title="@string/add_an_exponential_zoom_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_interval_zoom_function"
- android:title="Add an interval zoom function"
+ android:title="@string/add_an_interval_zoom_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_categorical_source_function"
- android:title="Add a categorical source function"
+ android:title="@string/add_a_categorical_source_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_exponential_source_function"
- android:title="Add an exponential source function"
+ android:title="@string/add_an_exponential_source_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_identity_source_function"
- android:title="Add an identity source function"
+ android:title="@string/add_an_identity_source_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_interval_source_function"
- android:title="Add an interval source function"
+ android:title="@string/add_an_interval_source_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_composite_exponential_function"
- android:title="Add a composite, exponential function"
+ android:title="@string/add_a_composite_exponential_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_composite_categorical_function"
- android:title="Add a composite, categorical function"
+ android:title="@string/add_a_composite_categorical_function"
mapbox:showAsAction="never"/>
<item
android:id="@+id/action_add_composite_interval_function"
- android:title="Add a composite, interval function"
+ android:title="@string/add_a_composite_interval_function"
mapbox:showAsAction="never"/>
</menu>
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
new file mode 100644
index 0000000000..613bdd21ef
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml
@@ -0,0 +1,12 @@
+<?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">
+ <item
+ 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_location_engine.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_engine.xml
new file mode 100644
index 0000000000..dd7408df09
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_engine.xml
@@ -0,0 +1,20 @@
+<?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 e0052d4a8c..7132c0c2a9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml
@@ -3,10 +3,10 @@
xmlns:mapbox="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_user_tracking"
- android:title="My Location Tracking"
+ android:title="@string/my_location_tracking"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_bangalore"
- android:title="Bangalore"
+ android:title="@string/bangalore"
mapbox:showAsAction="never" />
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml
index 0b3a8e797e..7d20442c8c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml
@@ -4,6 +4,5 @@
<item
android:id="@+id/menuItemReset"
android:title="@string/menuitem_title_reset"
- app:showAsAction="always"
- />
+ app:showAsAction="always"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml
index 86f0b4faee..5c77179272 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml
@@ -4,66 +4,66 @@
<item
android:id="@+id/action_list_layers"
- android:title="List all layers in the style"
+ android:title="@string/list_all_layers_in_the_style"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_list_sources"
- android:title="List all sources in the style"
+ android:title="@string/list_all_sources_in_the_style"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_water_color"
- android:title="Color the water"
+ android:title="@string/color_the_water"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_background_opacity"
- android:title="Set background opacity"
+ android:title="@string/set_background_opacity"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_road_avoid_edges"
- android:title="Set road symbol placement to Point"
+ android:title="@string/set_road_symbol_placement_to_point"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_layer_visibility"
- android:title="Set layer visibility to false"
+ android:title="@string/set_layer_visibility_to_false"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_parks_layer"
- android:title="Add a parks layer"
+ android:title="@string/add_a_parks_layer"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_dynamic_parks_layer"
- android:title="Add a dynamic GeoJSON source"
+ android:title="@string/add_a_dynamic_geojson_source"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_remove_layer"
- android:title="Remove buildings layer"
+ android:title="@string/remove_buildings_layer"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_terrain_layer"
- android:title="Add a terrain layer"
+ android:title="@string/add_a_terrain_layer"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_satellite_layer"
- android:title="Add a satellite layer"
+ android:title="@string/add_a_satellite_layer"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_update_water_color_on_zoom"
- android:title="Change the water color on zoom"
+ android:title="@string/change_the_water_color_on_zoom"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_add_custom_tiles"
- android:title="Custom tiles"
+ android:title="@string/custom_tiles"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_fill_filter"
- android:title="Apply filtered fill"
+ android:title="@string/apply_filtered_fill"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_line_filter"
- android:title="Apply filtered line"
+ android:title="@string/apply_filtered_line"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_numeric_filter"
- android:title="Apply numeric fill filter"
+ android:title="@string/apply_numeric_fill_filter"
mapbox:showAsAction="never" />
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml
index 77468b4861..8f396b07b0 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml
@@ -3,14 +3,14 @@
<item
android:id="@+id/action_toggle_text_size"
- android:title="Toggle text size"/>
+ android:title="@string/toggle_text_size"/>
<item
android:id="@+id/action_toggle_text_field"
- android:title="Toggle text field contents"/>
+ android:title="@string/toggle_text_field_contents"/>
<item
android:id="@+id/action_toggle_text_font"
- android:title="Toggle text font"/>
+ android:title="@string/toggle_text_font"/>
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml
new file mode 100644
index 0000000000..7f3c44262d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml
@@ -0,0 +1,12 @@
+<?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">
+ <item
+ android:id="@+id/menu_action_change_location"
+ android:title="@string/menuitem_change_location"
+ app:showAsAction="never"/>
+ <item
+ android:id="@+id/menu_action_toggle_source"
+ android:title="@string/menuitem_toggle_symbol_layer_visibility"
+ app:showAsAction="never"/>
+</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml
index 0cea519a24..67c0b2df55 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml
@@ -3,23 +3,23 @@
xmlns:mapbox="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_zoom_in"
- android:title="Zoom in"
+ android:title="@string/zoom_in"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_zoom_out"
- android:title="Zoom out"
+ android:title="@string/zoom_out"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_zoom_by"
- android:title="Zoom by 2"
+ android:title="@string/zoom_by_2"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_zoom_to_point"
- android:title="Zoom to point"
+ android:title="@string/zoom_to_point"
mapbox:showAsAction="never" />
<item
android:id="@+id/action_zoom_to"
- android:title="Zoom to 4"
+ android:title="@string/zoom_to_4"
mapbox:showAsAction="never" />
</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json
new file mode 100644
index 0000000000..f2cd969be4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json
@@ -0,0 +1,31 @@
+{
+ "version": 8,
+ "name": "Satellite",
+ "metadata": {
+ "mapbox:autocomposite": true
+ },
+ "sources": {
+ "mapbox": {
+ "type": "raster",
+ "url": "mapbox://mapbox.satellite",
+ "tileSize": 256
+ }
+ },
+ "sprite": "mapbox://sprites/mapbox/satellite-v8",
+ "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "rgb(4,7,14)"
+ }
+ },
+ {
+ "id": "satellite",
+ "type": "raster",
+ "source": "mapbox",
+ "source-layer": "mapbox_satellite_full"
+ }
+ ]
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson
new file mode 100644
index 0000000000..caff2ac81c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson
@@ -0,0 +1,2741 @@
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 1,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "Vanuatu",
+ "sov_a3": "VUT",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Vanuatu",
+ "adm0_a3": "VUT",
+ "geou_dif": 0,
+ "geounit": "Vanuatu",
+ "gu_a3": "VUT",
+ "su_dif": 0,
+ "subunit": "Vanuatu",
+ "su_a3": "VUT",
+ "brk_diff": 0,
+ "name": "Vanuatu",
+ "name_long": "Vanuatu",
+ "brk_a3": "VUT",
+ "brk_name": "Vanuatu",
+ "brk_group": null,
+ "abbrev": "Van.",
+ "postal": "VU",
+ "formal_en": "Republic of Vanuatu",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Vanuatu",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 3,
+ "mapcolor9": 7,
+ "mapcolor13": 3,
+ "pop_est": 218519,
+ "gdp_md_est": 988.5,
+ "pop_year": -99,
+ "lastcensus": 2009,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "VU",
+ "iso_a3": "VUT",
+ "iso_n3": "548",
+ "un_a3": "548",
+ "wb_a2": "VU",
+ "wb_a3": "VUT",
+ "woe_id": -99,
+ "adm0_a3_is": "VUT",
+ "adm0_a3_us": "VUT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Melanesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 166.9270664395989,
+ -15.367957152169708
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 1,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "France",
+ "sov_a3": "FR1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "French Southern and Antarctic Lands",
+ "adm0_a3": "ATF",
+ "geou_dif": 0,
+ "geounit": "French Southern and Antarctic Lands",
+ "gu_a3": "ATF",
+ "su_dif": 0,
+ "subunit": "French Southern and Antarctic Lands",
+ "su_a3": "ATF",
+ "brk_diff": 0,
+ "name": "Fr. S. Antarctic Lands",
+ "name_long": "French Southern and Antarctic Lands",
+ "brk_a3": "ATF",
+ "brk_name": "Fr. S. and Antarctic Lands",
+ "brk_group": null,
+ "abbrev": "Fr. S.A.L.",
+ "postal": "TF",
+ "formal_en": "Territory of the French Southern and Antarctic Lands",
+ "formal_fr": null,
+ "note_adm0": "Fr.",
+ "note_brk": null,
+ "name_sort": "French Southern and Antarctic Lands",
+ "name_alt": null,
+ "mapcolor7": 7,
+ "mapcolor8": 5,
+ "mapcolor9": 9,
+ "mapcolor13": 11,
+ "pop_est": 140,
+ "gdp_md_est": 16,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "TF",
+ "iso_a3": "ATF",
+ "iso_n3": "260",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "ATF",
+ "adm0_a3_us": "ATF",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Seven seas (open ocean)",
+ "subregion": "Seven seas (open ocean)",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 22,
+ "long_len": 35,
+ "abbrev_len": 10,
+ "tiny": 2,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 69.22513999086925,
+ -49.33878196163545
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "New Zealand",
+ "sov_a3": "NZ1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Cook Islands",
+ "adm0_a3": "COK",
+ "geou_dif": 0,
+ "geounit": "Cook Islands",
+ "gu_a3": "COK",
+ "su_dif": 0,
+ "subunit": "Cook Islands",
+ "su_a3": "COK",
+ "brk_diff": 0,
+ "name": "Cook Is.",
+ "name_long": "Cook Islands",
+ "brk_a3": "COK",
+ "brk_name": "Cook Is.",
+ "brk_group": null,
+ "abbrev": "Cook Is.",
+ "postal": "CK",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Assoc. with N.Z.",
+ "note_brk": null,
+ "name_sort": "Cook Islands",
+ "name_alt": null,
+ "mapcolor7": 3,
+ "mapcolor8": 3,
+ "mapcolor9": 4,
+ "mapcolor13": 4,
+ "pop_est": 11870,
+ "gdp_md_est": 183.2,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "CK",
+ "iso_a3": "COK",
+ "iso_n3": "184",
+ "un_a3": "184",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "COK",
+ "adm0_a3_us": "COK",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 8,
+ "long_len": 12,
+ "abbrev_len": 8,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -159.78922694470387,
+ -21.220086945691605
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "Samoa",
+ "sov_a3": "WSM",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Samoa",
+ "adm0_a3": "WSM",
+ "geou_dif": 0,
+ "geounit": "Samoa",
+ "gu_a3": "WSM",
+ "su_dif": 0,
+ "subunit": "Samoa",
+ "su_a3": "WSM",
+ "brk_diff": 0,
+ "name": "Samoa",
+ "name_long": "Samoa",
+ "brk_a3": "WSM",
+ "brk_name": "Samoa",
+ "brk_group": null,
+ "abbrev": "Samoa",
+ "postal": "WS",
+ "formal_en": "Independent State of Samoa",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Samoa",
+ "name_alt": null,
+ "mapcolor7": 3,
+ "mapcolor8": 3,
+ "mapcolor9": 4,
+ "mapcolor13": 6,
+ "pop_est": 219998,
+ "gdp_md_est": 1049,
+ "pop_year": -99,
+ "lastcensus": 2006,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "WS",
+ "iso_a3": "WSM",
+ "iso_n3": "882",
+ "un_a3": "882",
+ "wb_a2": "WS",
+ "wb_a3": "WSM",
+ "woe_id": -99,
+ "adm0_a3_is": "WSM",
+ "adm0_a3_us": "WSM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": -99,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -172.41373026688336,
+ -13.637369985140253
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 4,
+ "sovereignt": "Tonga",
+ "sov_a3": "TON",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Tonga",
+ "adm0_a3": "TON",
+ "geou_dif": 0,
+ "geounit": "Tonga",
+ "gu_a3": "TON",
+ "su_dif": 0,
+ "subunit": "Tonga",
+ "su_a3": "TON",
+ "brk_diff": 0,
+ "name": "Tonga",
+ "name_long": "Tonga",
+ "brk_a3": "TON",
+ "brk_name": "Tonga",
+ "brk_group": null,
+ "abbrev": "Tongo",
+ "postal": "TO",
+ "formal_en": "Kingdom of Tonga",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Tonga",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 1,
+ "mapcolor9": 1,
+ "mapcolor13": 8,
+ "pop_est": 120898,
+ "gdp_md_est": 549,
+ "pop_year": -99,
+ "lastcensus": 2006,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "TO",
+ "iso_a3": "TON",
+ "iso_n3": "776",
+ "un_a3": "776",
+ "wb_a2": "TO",
+ "wb_a3": "TON",
+ "woe_id": -99,
+ "adm0_a3_is": "TON",
+ "adm0_a3_us": "TON",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -175.23533295466754,
+ -21.158187998515473
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 5,
+ "sovereignt": "France",
+ "sov_a3": "FR1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "French Polynesia",
+ "adm0_a3": "PYF",
+ "geou_dif": 0,
+ "geounit": "French Polynesia",
+ "gu_a3": "PYF",
+ "su_dif": 0,
+ "subunit": "French Polynesia",
+ "su_a3": "PYF",
+ "brk_diff": 0,
+ "name": "Fr. Polynesia",
+ "name_long": "French Polynesia",
+ "brk_a3": "PYF",
+ "brk_name": "Fr. Polynesia",
+ "brk_group": null,
+ "abbrev": "Fr. Poly.",
+ "postal": "PF",
+ "formal_en": "French Polynesia",
+ "formal_fr": null,
+ "note_adm0": "Fr.",
+ "note_brk": null,
+ "name_sort": "French Polynesia",
+ "name_alt": null,
+ "mapcolor7": 7,
+ "mapcolor8": 5,
+ "mapcolor9": 9,
+ "mapcolor13": 11,
+ "pop_est": 287032,
+ "gdp_md_est": 4718,
+ "pop_year": -99,
+ "lastcensus": 2007,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "PF",
+ "iso_a3": "PYF",
+ "iso_n3": "258",
+ "un_a3": "258",
+ "wb_a2": "PF",
+ "wb_a3": "PYF",
+ "woe_id": -99,
+ "adm0_a3_is": "PYF",
+ "adm0_a3_us": "PYF",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 13,
+ "long_len": 16,
+ "abbrev_len": 9,
+ "tiny": 2,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -149.47549597877855,
+ -17.6250049835121
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Pitcairn Islands",
+ "adm0_a3": "PCN",
+ "geou_dif": 0,
+ "geounit": "Pitcairn Islands",
+ "gu_a3": "PCN",
+ "su_dif": 0,
+ "subunit": "Pitcairn Islands",
+ "su_a3": "PCN",
+ "brk_diff": 0,
+ "name": "Pitcairn Is.",
+ "name_long": "Pitcairn Islands",
+ "brk_a3": "PCN",
+ "brk_name": "Pitcairn Is.",
+ "brk_group": null,
+ "abbrev": "Pit. Is.",
+ "postal": "PN",
+ "formal_en": "Pitcairn, Henderson, Ducie and Oeno Islands",
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "Pitcairn Islands",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 48,
+ "gdp_md_est": 0.72,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "5. Low income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "PN",
+ "iso_a3": "PCN",
+ "iso_n3": "612",
+ "un_a3": "612",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "PCN",
+ "adm0_a3_us": "PCN",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 12,
+ "long_len": 16,
+ "abbrev_len": 8,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -128.31780012096033,
+ -24.364139777771015
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 5,
+ "sovereignt": "Barbados",
+ "sov_a3": "BRB",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Barbados",
+ "adm0_a3": "BRB",
+ "geou_dif": 0,
+ "geounit": "Barbados",
+ "gu_a3": "BRB",
+ "su_dif": 0,
+ "subunit": "Barbados",
+ "su_a3": "BRB",
+ "brk_diff": 0,
+ "name": "Barbados",
+ "name_long": "Barbados",
+ "brk_a3": "BRB",
+ "brk_name": "Barbados",
+ "brk_group": null,
+ "abbrev": "Barb.",
+ "postal": "BB",
+ "formal_en": "Barbados",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Barbados",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 1,
+ "mapcolor9": 5,
+ "mapcolor13": 3,
+ "pop_est": 284589,
+ "gdp_md_est": 5425,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "BB",
+ "iso_a3": "BRB",
+ "iso_n3": "052",
+ "un_a3": "052",
+ "wb_a2": "BB",
+ "wb_a3": "BRB",
+ "woe_id": -99,
+ "adm0_a3_is": "BRB",
+ "adm0_a3_us": "BRB",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "North America",
+ "region_un": "Americas",
+ "subregion": "Caribbean",
+ "region_wb": "Latin America & Caribbean",
+ "name_len": 8,
+ "long_len": 8,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -59.554305983838844,
+ 13.174672374462602
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 5,
+ "sovereignt": "Trinidad and Tobago",
+ "sov_a3": "TTO",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Trinidad and Tobago",
+ "adm0_a3": "TTO",
+ "geou_dif": 0,
+ "geounit": "Trinidad and Tobago",
+ "gu_a3": "TTO",
+ "su_dif": 0,
+ "subunit": "Trinidad and Tobago",
+ "su_a3": "TTO",
+ "brk_diff": 0,
+ "name": "Trinidad and Tobago",
+ "name_long": "Trinidad and Tobago",
+ "brk_a3": "TTO",
+ "brk_name": "Trinidad and Tobago",
+ "brk_group": null,
+ "abbrev": "Tr.T.",
+ "postal": "TT",
+ "formal_en": "Republic of Trinidad and Tobago",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Trinidad and Tobago",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 6,
+ "mapcolor9": 2,
+ "mapcolor13": 5,
+ "pop_est": 1310000,
+ "gdp_md_est": 29010,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "TT",
+ "iso_a3": "TTO",
+ "iso_n3": "780",
+ "un_a3": "780",
+ "wb_a2": "TT",
+ "wb_a3": "TTO",
+ "woe_id": -99,
+ "adm0_a3_is": "TTO",
+ "adm0_a3_us": "TTO",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "North America",
+ "region_un": "Americas",
+ "subregion": "Caribbean",
+ "region_wb": "Latin America & Caribbean",
+ "name_len": 19,
+ "long_len": 19,
+ "abbrev_len": 5,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -61.255188941565905,
+ 10.43680324164859
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 4,
+ "sovereignt": "Sao Tome and Principe",
+ "sov_a3": "STP",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Sao Tome and Principe",
+ "adm0_a3": "STP",
+ "geou_dif": 0,
+ "geounit": "Sao Tome and Principe",
+ "gu_a3": "STP",
+ "su_dif": 0,
+ "subunit": "Sao Tome and Principe",
+ "su_a3": "STP",
+ "brk_diff": 0,
+ "name": "São Tomé and Principe",
+ "name_long": "São Tomé and Principe",
+ "brk_a3": "STP",
+ "brk_name": "Sao Tome and Principe",
+ "brk_group": null,
+ "abbrev": "S.T.P.",
+ "postal": "ST",
+ "formal_en": "Democratic Republic of São Tomé and Principe",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "São Tomé and Principe",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 6,
+ "mapcolor9": 1,
+ "mapcolor13": 7,
+ "pop_est": 212679,
+ "gdp_md_est": 276.5,
+ "pop_year": -99,
+ "lastcensus": 2001,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "ST",
+ "iso_a3": "STP",
+ "iso_n3": "678",
+ "un_a3": "678",
+ "wb_a2": "ST",
+ "wb_a3": "STP",
+ "woe_id": -99,
+ "adm0_a3_is": "STP",
+ "adm0_a3_us": "STP",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Africa",
+ "region_un": "Africa",
+ "subregion": "Middle Africa",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 21,
+ "long_len": 21,
+ "abbrev_len": 6,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 6.617198520543866,
+ 0.246806952308191
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 4,
+ "type": "Geo subunit",
+ "admin": "Saint Helena",
+ "adm0_a3": "SHN",
+ "geou_dif": 0,
+ "geounit": "Saint Helena",
+ "gu_a3": "SHN",
+ "su_dif": 1,
+ "subunit": "Ascension",
+ "su_a3": "BAC",
+ "brk_diff": 0,
+ "name": "Ascension",
+ "name_long": "Ascension",
+ "brk_a3": "BAC",
+ "brk_name": "Ascension",
+ "brk_group": null,
+ "abbrev": "Asc.",
+ "postal": "AS",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "Ascension",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 940,
+ "gdp_md_est": 2.21553,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "-99",
+ "income_grp": "-99",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SHN",
+ "adm0_a3_us": "SHN",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Seven seas (open ocean)",
+ "subregion": "Seven seas (open ocean)",
+ "region_wb": "Antarctica",
+ "name_len": 9,
+ "long_len": 9,
+ "abbrev_len": 4,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -14.362068334482444,
+ -7.939246540570252
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Saint Helena",
+ "adm0_a3": "SHN",
+ "geou_dif": 0,
+ "geounit": "Saint Helena",
+ "gu_a3": "SHN",
+ "su_dif": 0,
+ "subunit": "Saint Helena",
+ "su_a3": "SHN",
+ "brk_diff": 0,
+ "name": "Saint Helena",
+ "name_long": "Saint Helena",
+ "brk_a3": "SHN",
+ "brk_name": "Saint Helena",
+ "brk_group": null,
+ "abbrev": "St.H.",
+ "postal": "SH",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "St. Helena",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 7637,
+ "gdp_md_est": 18,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "SH",
+ "iso_a3": "SHN",
+ "iso_n3": "654",
+ "un_a3": "654",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SHN",
+ "adm0_a3_us": "SHN",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Africa",
+ "subregion": "Western Africa",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 12,
+ "long_len": 12,
+ "abbrev_len": 5,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -5.716296101395358,
+ -15.963221612123107
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 5,
+ "sovereignt": "Malta",
+ "sov_a3": "MLT",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Malta",
+ "adm0_a3": "MLT",
+ "geou_dif": 0,
+ "geounit": "Malta",
+ "gu_a3": "MLT",
+ "su_dif": 0,
+ "subunit": "Malta",
+ "su_a3": "MLT",
+ "brk_diff": 0,
+ "name": "Malta",
+ "name_long": "Malta",
+ "brk_a3": "MLT",
+ "brk_name": "Malta",
+ "brk_group": null,
+ "abbrev": "Malta",
+ "postal": "M",
+ "formal_en": "Republic of Malta",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Malta",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 4,
+ "mapcolor9": 1,
+ "mapcolor13": 8,
+ "pop_est": 405165,
+ "gdp_md_est": 9962,
+ "pop_year": -99,
+ "lastcensus": 2005,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MT",
+ "iso_a3": "MLT",
+ "iso_n3": "470",
+ "un_a3": "470",
+ "wb_a2": "MT",
+ "wb_a3": "MLT",
+ "woe_id": -99,
+ "adm0_a3_is": "MLT",
+ "adm0_a3_us": "MLT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Europe",
+ "region_un": "Europe",
+ "subregion": "Southern Europe",
+ "region_wb": "Middle East & North Africa",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 14.438179478988388,
+ 35.882081031796645
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 2,
+ "sovereignt": "Bahrain",
+ "sov_a3": "BHR",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Bahrain",
+ "adm0_a3": "BHR",
+ "geou_dif": 0,
+ "geounit": "Bahrain",
+ "gu_a3": "BHR",
+ "su_dif": 0,
+ "subunit": "Bahrain",
+ "su_a3": "BHR",
+ "brk_diff": 0,
+ "name": "Bahrain",
+ "name_long": "Bahrain",
+ "brk_a3": "BHR",
+ "brk_name": "Bahrain",
+ "brk_group": null,
+ "abbrev": "Bahr.",
+ "postal": "BH",
+ "formal_en": "Kingdom of Bahrain",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Bahrain",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 1,
+ "mapcolor9": 1,
+ "mapcolor13": 9,
+ "pop_est": 727785,
+ "gdp_md_est": 26820,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "BH",
+ "iso_a3": "BHR",
+ "iso_n3": "048",
+ "un_a3": "048",
+ "wb_a2": "BH",
+ "wb_a3": "BHR",
+ "woe_id": -99,
+ "adm0_a3_is": "BHR",
+ "adm0_a3_us": "BHR",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Asia",
+ "region_un": "Asia",
+ "subregion": "Western Asia",
+ "region_wb": "Middle East & North Africa",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 5,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 50.553638136605,
+ 26.06944265390905
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "Maldives",
+ "sov_a3": "MDV",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Maldives",
+ "adm0_a3": "MDV",
+ "geou_dif": 0,
+ "geounit": "Maldives",
+ "gu_a3": "MDV",
+ "su_dif": 0,
+ "subunit": "Maldives",
+ "su_a3": "MDV",
+ "brk_diff": 0,
+ "name": "Maldives",
+ "name_long": "Maldives",
+ "brk_a3": "MDV",
+ "brk_name": "Maldives",
+ "brk_group": null,
+ "abbrev": "Mald.",
+ "postal": "MV",
+ "formal_en": "Republic of Maldives",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Maldives",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 3,
+ "mapcolor9": 1,
+ "mapcolor13": 7,
+ "pop_est": 396334,
+ "gdp_md_est": 1716,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MV",
+ "iso_a3": "MDV",
+ "iso_n3": "462",
+ "un_a3": "462",
+ "wb_a2": "MV",
+ "wb_a3": "MDV",
+ "woe_id": -99,
+ "adm0_a3_is": "MDV",
+ "adm0_a3_us": "B13",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Asia",
+ "subregion": "Southern Asia",
+ "region_wb": "South Asia",
+ "name_len": 8,
+ "long_len": 8,
+ "abbrev_len": 5,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 73.50223056083513,
+ 4.186658727806048
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "British Indian Ocean Territory",
+ "adm0_a3": "IOT",
+ "geou_dif": 0,
+ "geounit": "British Indian Ocean Territory",
+ "gu_a3": "IOT",
+ "su_dif": 0,
+ "subunit": "British Indian Ocean Territory",
+ "su_a3": "IOT",
+ "brk_diff": 1,
+ "name": "Br. Indian Ocean Ter.",
+ "name_long": "British Indian Ocean Territory",
+ "brk_a3": "B69",
+ "brk_name": "Br. Indian Ocean Ter.",
+ "brk_group": null,
+ "abbrev": "I.O.T.",
+ "postal": "IO",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": "Admin. by U.K.; Claimed by Mauritius and Seychelles",
+ "name_sort": "British Indian Ocean Territory",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 4000,
+ "gdp_md_est": 160,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "IO",
+ "iso_a3": "IOT",
+ "iso_n3": "086",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "IOT",
+ "adm0_a3_us": "IOT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Seven seas (open ocean)",
+ "subregion": "Seven seas (open ocean)",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 21,
+ "long_len": 30,
+ "abbrev_len": 6,
+ "tiny": 5,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 72.47872949418257,
+ -7.340705873210993
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 3,
+ "sovereignt": "Singapore",
+ "sov_a3": "SGP",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Singapore",
+ "adm0_a3": "SGP",
+ "geou_dif": 0,
+ "geounit": "Singapore",
+ "gu_a3": "SGP",
+ "su_dif": 0,
+ "subunit": "Singapore",
+ "su_a3": "SGP",
+ "brk_diff": 0,
+ "name": "Singapore",
+ "name_long": "Singapore",
+ "brk_a3": "SGP",
+ "brk_name": "Singapore",
+ "brk_group": null,
+ "abbrev": "Sing.",
+ "postal": "SG",
+ "formal_en": "Republic of Singapore",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Singapore",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 3,
+ "mapcolor9": 7,
+ "mapcolor13": 3,
+ "pop_est": 4657542,
+ "gdp_md_est": 237300,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "SG",
+ "iso_a3": "SGP",
+ "iso_n3": "702",
+ "un_a3": "702",
+ "wb_a2": "SG",
+ "wb_a3": "SGP",
+ "woe_id": -99,
+ "adm0_a3_is": "SGP",
+ "adm0_a3_us": "SGP",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Asia",
+ "region_un": "Asia",
+ "subregion": "South-Eastern Asia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 9,
+ "long_len": 9,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 103.81481982900323,
+ 1.359363931813562
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 2,
+ "sovereignt": "Brunei",
+ "sov_a3": "BRN",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Brunei",
+ "adm0_a3": "BRN",
+ "geou_dif": 0,
+ "geounit": "Brunei",
+ "gu_a3": "BRN",
+ "su_dif": 0,
+ "subunit": "Brunei",
+ "su_a3": "BRN",
+ "brk_diff": 0,
+ "name": "Brunei",
+ "name_long": "Brunei Darussalam",
+ "brk_a3": "BRN",
+ "brk_name": "Brunei",
+ "brk_group": null,
+ "abbrev": "Brunei",
+ "postal": "BN",
+ "formal_en": "Negara Brunei Darussalam",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Brunei",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 12,
+ "pop_est": 388190,
+ "gdp_md_est": 20250,
+ "pop_year": -99,
+ "lastcensus": 2001,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "BN",
+ "iso_a3": "BRN",
+ "iso_n3": "096",
+ "un_a3": "096",
+ "wb_a2": "BN",
+ "wb_a3": "BRN",
+ "woe_id": -99,
+ "adm0_a3_is": "BRN",
+ "adm0_a3_us": "BRN",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Asia",
+ "region_un": "Asia",
+ "subregion": "South-Eastern Asia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 6,
+ "long_len": 17,
+ "abbrev_len": 6,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 114.56745460338925,
+ 4.434669496170784
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Palau",
+ "sov_a3": "PLW",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Palau",
+ "adm0_a3": "PLW",
+ "geou_dif": 0,
+ "geounit": "Palau",
+ "gu_a3": "PLW",
+ "su_dif": 0,
+ "subunit": "Palau",
+ "su_a3": "PLW",
+ "brk_diff": 0,
+ "name": "Palau",
+ "name_long": "Palau",
+ "brk_a3": "PLW",
+ "brk_name": "Palau",
+ "brk_group": null,
+ "abbrev": "Palau",
+ "postal": "PW",
+ "formal_en": "Republic of Palau",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Palau",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 5,
+ "mapcolor9": 1,
+ "mapcolor13": 12,
+ "pop_est": 20796,
+ "gdp_md_est": 164,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "PW",
+ "iso_a3": "PLW",
+ "iso_n3": "585",
+ "un_a3": "585",
+ "wb_a2": "PW",
+ "wb_a3": "PLW",
+ "woe_id": -99,
+ "adm0_a3_is": "PLW",
+ "adm0_a3_us": "PLW",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 134.57924133620793,
+ 7.507494163314107
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "United States of America",
+ "sov_a3": "US1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Northern Mariana Islands",
+ "adm0_a3": "MNP",
+ "geou_dif": 0,
+ "geounit": "Northern Mariana Islands",
+ "gu_a3": "MNP",
+ "su_dif": 0,
+ "subunit": "Northern Mariana Islands",
+ "su_a3": "MNP",
+ "brk_diff": 0,
+ "name": "N. Mariana Is.",
+ "name_long": "Northern Mariana Islands",
+ "brk_a3": "MNP",
+ "brk_name": "N. Mariana Is.",
+ "brk_group": null,
+ "abbrev": "N.M.I.",
+ "postal": "MP",
+ "formal_en": "Commonwealth of the Northern Mariana Islands",
+ "formal_fr": null,
+ "note_adm0": "Commonwealth of U.S.A.",
+ "note_brk": null,
+ "name_sort": "Northern Mariana Islands",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 5,
+ "mapcolor9": 1,
+ "mapcolor13": 1,
+ "pop_est": 88662,
+ "gdp_md_est": 900,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MP",
+ "iso_a3": "MNP",
+ "iso_n3": "580",
+ "un_a3": "580",
+ "wb_a2": "MP",
+ "wb_a3": "MNP",
+ "woe_id": -99,
+ "adm0_a3_is": "MNP",
+ "adm0_a3_us": "MNP",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 14,
+ "long_len": 24,
+ "abbrev_len": 6,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 145.73926332724704,
+ 15.17463695328189
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "United States of America",
+ "sov_a3": "US1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Guam",
+ "adm0_a3": "GUM",
+ "geou_dif": 0,
+ "geounit": "Guam",
+ "gu_a3": "GUM",
+ "su_dif": 0,
+ "subunit": "Guam",
+ "su_a3": "GUM",
+ "brk_diff": 0,
+ "name": "Guam",
+ "name_long": "Guam",
+ "brk_a3": "GUM",
+ "brk_name": "Guam",
+ "brk_group": null,
+ "abbrev": "Guam",
+ "postal": "GU",
+ "formal_en": "Territory of Guam",
+ "formal_fr": null,
+ "note_adm0": "U.S.A.",
+ "note_brk": null,
+ "name_sort": "Guam",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 5,
+ "mapcolor9": 1,
+ "mapcolor13": 1,
+ "pop_est": 178430,
+ "gdp_md_est": 2500,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "GU",
+ "iso_a3": "GUM",
+ "iso_n3": "316",
+ "un_a3": "316",
+ "wb_a2": "GU",
+ "wb_a3": "GUM",
+ "woe_id": -99,
+ "adm0_a3_is": "GUM",
+ "adm0_a3_us": "GUM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 4,
+ "long_len": 4,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 144.77003842181864,
+ 13.459684857600507
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "Federated States of Micronesia",
+ "sov_a3": "FSM",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Federated States of Micronesia",
+ "adm0_a3": "FSM",
+ "geou_dif": 0,
+ "geounit": "Federated States of Micronesia",
+ "gu_a3": "FSM",
+ "su_dif": 0,
+ "subunit": "Federated States of Micronesia",
+ "su_a3": "FSM",
+ "brk_diff": 0,
+ "name": "Micronesia",
+ "name_long": "Federated States of Micronesia",
+ "brk_a3": "FSM",
+ "brk_name": "Micronesia",
+ "brk_group": null,
+ "abbrev": "F.S.M.",
+ "postal": "FSM",
+ "formal_en": "Federated States of Micronesia",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Micronesia, Federated States of",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 2,
+ "mapcolor9": 4,
+ "mapcolor13": 13,
+ "pop_est": 107434,
+ "gdp_md_est": 238.1,
+ "pop_year": -99,
+ "lastcensus": 2000,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "FM",
+ "iso_a3": "FSM",
+ "iso_n3": "583",
+ "un_a3": "583",
+ "wb_a2": "FM",
+ "wb_a3": "FSM",
+ "woe_id": -99,
+ "adm0_a3_is": "FSM",
+ "adm0_a3_us": "FSM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 10,
+ "long_len": 30,
+ "abbrev_len": 6,
+ "tiny": -99,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 158.2420151934607,
+ 6.885941535379288
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Marshall Islands",
+ "sov_a3": "MHL",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Marshall Islands",
+ "adm0_a3": "MHL",
+ "geou_dif": 0,
+ "geounit": "Marshall Islands",
+ "gu_a3": "MHL",
+ "su_dif": 0,
+ "subunit": "Marshall Islands",
+ "su_a3": "MHL",
+ "brk_diff": 0,
+ "name": "Marshall Is.",
+ "name_long": "Marshall Islands",
+ "brk_a3": "MHL",
+ "brk_name": "Marshall Is.",
+ "brk_group": null,
+ "abbrev": "M. Is.",
+ "postal": "MH",
+ "formal_en": "Republic of the Marshall Islands",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Marshall Islands",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 5,
+ "mapcolor9": 5,
+ "mapcolor13": 3,
+ "pop_est": 64522,
+ "gdp_md_est": 133.5,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MH",
+ "iso_a3": "MHL",
+ "iso_n3": "584",
+ "un_a3": "584",
+ "wb_a2": "MH",
+ "wb_a3": "MHL",
+ "woe_id": -99,
+ "adm0_a3_is": "MHL",
+ "adm0_a3_us": "MHL",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 12,
+ "long_len": 16,
+ "abbrev_len": 6,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 168.72896600641184,
+ 7.313460144816133
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Kiribati",
+ "sov_a3": "KIR",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Kiribati",
+ "adm0_a3": "KIR",
+ "geou_dif": 0,
+ "geounit": "Kiribati",
+ "gu_a3": "KIR",
+ "su_dif": 0,
+ "subunit": "Kiribati",
+ "su_a3": "KIR",
+ "brk_diff": 0,
+ "name": "Kiribati",
+ "name_long": "Kiribati",
+ "brk_a3": "KIR",
+ "brk_name": "Kiribati",
+ "brk_group": null,
+ "abbrev": "Kir.",
+ "postal": "KI",
+ "formal_en": "Republic of Kiribati",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Kiribati",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 7,
+ "mapcolor9": 6,
+ "mapcolor13": 12,
+ "pop_est": 112850,
+ "gdp_md_est": 579.5,
+ "pop_year": -99,
+ "lastcensus": 2005,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "KI",
+ "iso_a3": "KIR",
+ "iso_n3": "296",
+ "un_a3": "296",
+ "wb_a2": "KI",
+ "wb_a3": "KIR",
+ "woe_id": -99,
+ "adm0_a3_is": "KIR",
+ "adm0_a3_us": "KIR",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 8,
+ "long_len": 8,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 173.13515838316619,
+ 1.364258124187756
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "Nauru",
+ "sov_a3": "NRU",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Nauru",
+ "adm0_a3": "NRU",
+ "geou_dif": 0,
+ "geounit": "Nauru",
+ "gu_a3": "NRU",
+ "su_dif": 0,
+ "subunit": "Nauru",
+ "su_a3": "NRU",
+ "brk_diff": 0,
+ "name": "Nauru",
+ "name_long": "Nauru",
+ "brk_a3": "NRU",
+ "brk_name": "Nauru",
+ "brk_group": null,
+ "abbrev": "Nauru",
+ "postal": "NR",
+ "formal_en": "Republic of Nauru",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Nauru",
+ "name_alt": null,
+ "mapcolor7": 3,
+ "mapcolor8": 7,
+ "mapcolor9": 6,
+ "mapcolor13": 9,
+ "pop_est": 14019,
+ "gdp_md_est": 60,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "4. Lower middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "NR",
+ "iso_a3": "NRU",
+ "iso_n3": "520",
+ "un_a3": "520",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "NRU",
+ "adm0_a3_us": "NRU",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Micronesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 5,
+ "long_len": 5,
+ "abbrev_len": 5,
+ "tiny": 3,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 166.93748256244703,
+ -0.523068535976108
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 5,
+ "sr_label_o": 6,
+ "sovereignt": "Tuvalu",
+ "sov_a3": "TUV",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Tuvalu",
+ "adm0_a3": "TUV",
+ "geou_dif": 0,
+ "geounit": "Tuvalu",
+ "gu_a3": "TUV",
+ "su_dif": 0,
+ "subunit": "Tuvalu",
+ "su_a3": "TUV",
+ "brk_diff": 0,
+ "name": "Tuvalu",
+ "name_long": "Tuvalu",
+ "brk_a3": "TUV",
+ "brk_name": "Tuvalu",
+ "brk_group": null,
+ "abbrev": "Tuv.",
+ "postal": "TV",
+ "formal_en": "Tuvalu",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Tuvalu",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 3,
+ "mapcolor9": 8,
+ "mapcolor13": 5,
+ "pop_est": 12373,
+ "gdp_md_est": 14.94,
+ "pop_year": -99,
+ "lastcensus": 2002,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "TV",
+ "iso_a3": "TUV",
+ "iso_n3": "798",
+ "un_a3": "798",
+ "wb_a2": "TV",
+ "wb_a3": "TUV",
+ "woe_id": -99,
+ "adm0_a3_is": "TUV",
+ "adm0_a3_us": "TUV",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Oceania",
+ "region_un": "Oceania",
+ "subregion": "Polynesia",
+ "region_wb": "East Asia & Pacific",
+ "name_len": 6,
+ "long_len": 6,
+ "abbrev_len": 4,
+ "tiny": 5,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries Pacific"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 179.20397422623353,
+ -8.49972371316585
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "Mauritius",
+ "sov_a3": "MUS",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Mauritius",
+ "adm0_a3": "MUS",
+ "geou_dif": 0,
+ "geounit": "Mauritius",
+ "gu_a3": "MUS",
+ "su_dif": 0,
+ "subunit": "Mauritius",
+ "su_a3": "MUS",
+ "brk_diff": 0,
+ "name": "Mauritius",
+ "name_long": "Mauritius",
+ "brk_a3": "MUS",
+ "brk_name": "Mauritius",
+ "brk_group": null,
+ "abbrev": "Mus.",
+ "postal": "MU",
+ "formal_en": "Republic of Mauritius",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Mauritius",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 3,
+ "mapcolor9": 5,
+ "mapcolor13": 7,
+ "pop_est": 1284264,
+ "gdp_md_est": 15270,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "6. Developing region",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "MU",
+ "iso_a3": "MUS",
+ "iso_n3": "480",
+ "un_a3": "480",
+ "wb_a2": "MU",
+ "wb_a3": "MUS",
+ "woe_id": -99,
+ "adm0_a3_is": "MUS",
+ "adm0_a3_us": "MUS",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Africa",
+ "subregion": "Eastern Africa",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 9,
+ "long_len": 9,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 57.58565995816849,
+ -20.302274672122962
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Comoros",
+ "sov_a3": "COM",
+ "adm0_dif": 0,
+ "level": 2,
+ "type": "Sovereign country",
+ "admin": "Comoros",
+ "adm0_a3": "COM",
+ "geou_dif": 0,
+ "geounit": "Comoros",
+ "gu_a3": "COM",
+ "su_dif": 0,
+ "subunit": "Comoros",
+ "su_a3": "COM",
+ "brk_diff": 0,
+ "name": "Comoros",
+ "name_long": "Comoros",
+ "brk_a3": "COM",
+ "brk_name": "Comoros",
+ "brk_group": null,
+ "abbrev": "Com.",
+ "postal": "KM",
+ "formal_en": "Union of the Comoros",
+ "formal_fr": null,
+ "note_adm0": null,
+ "note_brk": null,
+ "name_sort": "Comoros",
+ "name_alt": null,
+ "mapcolor7": 2,
+ "mapcolor8": 1,
+ "mapcolor9": 4,
+ "mapcolor13": 10,
+ "pop_est": 752438,
+ "gdp_md_est": 751.2,
+ "pop_year": -99,
+ "lastcensus": 2003,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "5. Low income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "KM",
+ "iso_a3": "COM",
+ "iso_n3": "174",
+ "un_a3": "174",
+ "wb_a2": "KM",
+ "wb_a3": "COM",
+ "woe_id": -99,
+ "adm0_a3_is": "COM",
+ "adm0_a3_us": "COM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Africa",
+ "region_un": "Africa",
+ "subregion": "Eastern Africa",
+ "region_wb": "Sub-Saharan Africa",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 4,
+ "tiny": 2,
+ "homepart": 1,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 43.337943198143535,
+ -11.715555516231973
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "Denmark",
+ "sov_a3": "DN1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Faroe Islands",
+ "adm0_a3": "FRO",
+ "geou_dif": 0,
+ "geounit": "Faroe Islands",
+ "gu_a3": "FRO",
+ "su_dif": 0,
+ "subunit": "Faroe Islands",
+ "su_a3": "FRO",
+ "brk_diff": 0,
+ "name": "Faeroe Is.",
+ "name_long": "Faeroe Islands",
+ "brk_a3": "FRO",
+ "brk_name": "Faeroe Islands",
+ "brk_group": null,
+ "abbrev": "Faeroe Is.",
+ "postal": "FO",
+ "formal_en": "Føroyar Is. (Faeroe Is.)",
+ "formal_fr": null,
+ "note_adm0": "Den.",
+ "note_brk": null,
+ "name_sort": "Faeroe Islands",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 1,
+ "mapcolor9": 3,
+ "mapcolor13": 12,
+ "pop_est": 48856,
+ "gdp_md_est": 1000,
+ "pop_year": -99,
+ "lastcensus": 2011,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "FO",
+ "iso_a3": "FRO",
+ "iso_n3": "234",
+ "un_a3": "234",
+ "wb_a2": "FO",
+ "wb_a3": "FRO",
+ "woe_id": -99,
+ "adm0_a3_is": "FRO",
+ "adm0_a3_us": "FRO",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Europe",
+ "region_un": "Europe",
+ "subregion": "Northern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 10,
+ "long_len": 14,
+ "abbrev_len": 10,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -6.942567803221323,
+ 62.19161776035833
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Norway",
+ "sov_a3": "NOR",
+ "adm0_dif": 0,
+ "level": 3,
+ "type": "Geo unit",
+ "admin": "Norway",
+ "adm0_a3": "NOR",
+ "geou_dif": 1,
+ "geounit": "Jan Mayen",
+ "gu_a3": "NJM",
+ "su_dif": 0,
+ "subunit": "Jan Mayen",
+ "su_a3": "NJM",
+ "brk_diff": 0,
+ "name": "Jan Mayen I.",
+ "name_long": "Jan Mayen Island",
+ "brk_a3": "NJM",
+ "brk_name": "Jan Mayen",
+ "brk_group": null,
+ "abbrev": "J.M.",
+ "postal": "JM",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Nor.",
+ "note_brk": null,
+ "name_sort": "Jan Mayen I.",
+ "name_alt": null,
+ "mapcolor7": 5,
+ "mapcolor8": 3,
+ "mapcolor9": 8,
+ "mapcolor13": 12,
+ "pop_est": 20,
+ "gdp_md_est": -99,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "5. Low income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SJM",
+ "adm0_a3_us": "NOR",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Europe",
+ "region_un": "Europe",
+ "subregion": "Northern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 12,
+ "long_len": 16,
+ "abbrev_len": 4,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny GeoUnit"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -8.420617438175157,
+ 71.02824880643254
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "France",
+ "sov_a3": "FR1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Saint Pierre and Miquelon",
+ "adm0_a3": "SPM",
+ "geou_dif": 0,
+ "geounit": "Saint Pierre and Miquelon",
+ "gu_a3": "SPM",
+ "su_dif": 0,
+ "subunit": "Saint Pierre and Miquelon",
+ "su_a3": "SPM",
+ "brk_diff": 0,
+ "name": "St. Pierre and Miquelon",
+ "name_long": "Saint Pierre and Miquelon",
+ "brk_a3": "SPM",
+ "brk_name": "St. Pierre and Miquelon",
+ "brk_group": null,
+ "abbrev": "St. P.M.",
+ "postal": "PM",
+ "formal_en": "Saint Pierre and Miquelon",
+ "formal_fr": null,
+ "note_adm0": "Fr.",
+ "note_brk": null,
+ "name_sort": "St. Pierre and Miquelon",
+ "name_alt": null,
+ "mapcolor7": 7,
+ "mapcolor8": 5,
+ "mapcolor9": 9,
+ "mapcolor13": 11,
+ "pop_est": 7051,
+ "gdp_md_est": 48.3,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "3. Upper middle income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "PM",
+ "iso_a3": "SPM",
+ "iso_n3": "666",
+ "un_a3": "666",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SPM",
+ "adm0_a3_us": "SPM",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "North America",
+ "region_un": "Americas",
+ "subregion": "Northern America",
+ "region_wb": "North America",
+ "name_len": 23,
+ "long_len": 25,
+ "abbrev_len": 8,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -56.31570304234327,
+ 46.85746558614022
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 4,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "Bermuda",
+ "adm0_a3": "BMU",
+ "geou_dif": 0,
+ "geounit": "Bermuda",
+ "gu_a3": "BMU",
+ "su_dif": 0,
+ "subunit": "Bermuda",
+ "su_a3": "BMU",
+ "brk_diff": 0,
+ "name": "Bermuda",
+ "name_long": "Bermuda",
+ "brk_a3": "BMU",
+ "brk_name": "Bermuda",
+ "brk_group": null,
+ "abbrev": "Berm.",
+ "postal": "BM",
+ "formal_en": "The Bermudas or Somers Isles",
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "Bermuda",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 67837,
+ "gdp_md_est": 4500,
+ "pop_year": -99,
+ "lastcensus": 2010,
+ "gdp_year": -99,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "2. High income: nonOECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "BM",
+ "iso_a3": "BMU",
+ "iso_n3": "060",
+ "un_a3": "060",
+ "wb_a2": "BM",
+ "wb_a3": "BMU",
+ "woe_id": -99,
+ "adm0_a3_is": "BMU",
+ "adm0_a3_us": "BMU",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "North America",
+ "region_un": "Americas",
+ "subregion": "Northern America",
+ "region_wb": "North America",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 5,
+ "tiny": 4,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -64.74797798630703,
+ 32.307221641280876
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Portugal",
+ "sov_a3": "PRT",
+ "adm0_dif": 0,
+ "level": 3,
+ "type": "Geo unit",
+ "admin": "Portugal",
+ "adm0_a3": "PRT",
+ "geou_dif": 1,
+ "geounit": "Azores",
+ "gu_a3": "PAZ",
+ "su_dif": 0,
+ "subunit": "Azores",
+ "su_a3": "PAZ",
+ "brk_diff": 0,
+ "name": "Azores",
+ "name_long": "Azores",
+ "brk_a3": "PAZ",
+ "brk_name": "Azores",
+ "brk_group": null,
+ "abbrev": "Az.",
+ "postal": "AZ",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Port.",
+ "note_brk": null,
+ "name_sort": "Azores",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 7,
+ "mapcolor9": 1,
+ "mapcolor13": 4,
+ "pop_est": 235374,
+ "gdp_md_est": 4492,
+ "pop_year": 0,
+ "lastcensus": -99,
+ "gdp_year": 0,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "1. High income: OECD",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "-99",
+ "adm0_a3_us": "PRT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Europe",
+ "subregion": "Southern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 6,
+ "long_len": 6,
+ "abbrev_len": 3,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny GeoUnit"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -28.423474244011175,
+ 38.48233011770992
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 2,
+ "sr_label_o": 6,
+ "sovereignt": "Spain",
+ "sov_a3": "ESP",
+ "adm0_dif": 0,
+ "level": 4,
+ "type": "Geo subunit",
+ "admin": "Spain",
+ "adm0_a3": "ESP",
+ "geou_dif": 0,
+ "geounit": "Spain",
+ "gu_a3": "ESP",
+ "su_dif": 1,
+ "subunit": "Canary Islands",
+ "su_a3": "ESC",
+ "brk_diff": 0,
+ "name": "Canary Is.",
+ "name_long": "Canary Islands",
+ "brk_a3": "ESC",
+ "brk_name": "Canary Is.",
+ "brk_group": null,
+ "abbrev": "Can. Is.",
+ "postal": "CI",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Sp.",
+ "note_brk": null,
+ "name_sort": "Canary Islands",
+ "name_alt": null,
+ "mapcolor7": 4,
+ "mapcolor8": 5,
+ "mapcolor9": 5,
+ "mapcolor13": 5,
+ "pop_est": 2098593,
+ "gdp_md_est": 72654.55481,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "-99",
+ "income_grp": "-99",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "ESP",
+ "adm0_a3_us": "ESP",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Africa",
+ "region_un": "Europe",
+ "subregion": "Southern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 10,
+ "long_len": 14,
+ "abbrev_len": 8,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny GeoSubunit"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -16.592772263568634,
+ 28.228989968662177
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "Portugal",
+ "sov_a3": "PRT",
+ "adm0_dif": 0,
+ "level": 3,
+ "type": "Geo unit",
+ "admin": "Portugal",
+ "adm0_a3": "PRT",
+ "geou_dif": 1,
+ "geounit": "Madeira",
+ "gu_a3": "PMD",
+ "su_dif": 0,
+ "subunit": "Madeira",
+ "su_a3": "PMD",
+ "brk_diff": 0,
+ "name": "Madeira",
+ "name_long": "Madeira",
+ "brk_a3": "PMD",
+ "brk_name": "Madeira",
+ "brk_group": null,
+ "abbrev": "Mad.",
+ "postal": "MD",
+ "formal_en": null,
+ "formal_fr": null,
+ "note_adm0": "Port.",
+ "note_brk": null,
+ "name_sort": "Madeira",
+ "name_alt": null,
+ "mapcolor7": 1,
+ "mapcolor8": 7,
+ "mapcolor9": 1,
+ "mapcolor13": 4,
+ "pop_est": 267785,
+ "gdp_md_est": 6414,
+ "pop_year": 0,
+ "lastcensus": -99,
+ "gdp_year": 0,
+ "economy": "2. Developed region: nonG7",
+ "income_grp": "1. High income: OECD",
+ "wikipedia": 0,
+ "fips_10": null,
+ "iso_a2": "-99",
+ "iso_a3": "-99",
+ "iso_n3": "-99",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "-99",
+ "adm0_a3_us": "PRT",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Africa",
+ "region_un": "Europe",
+ "subregion": "Southern Europe",
+ "region_wb": "Europe & Central Asia",
+ "name_len": 7,
+ "long_len": 7,
+ "abbrev_len": 4,
+ "tiny": -99,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny GeoUnit"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -16.959751345358598,
+ 32.74536514049669
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "scalerank": 2,
+ "sr_label_i": 3,
+ "sr_label_o": 6,
+ "sovereignt": "United Kingdom",
+ "sov_a3": "GB1",
+ "adm0_dif": 1,
+ "level": 2,
+ "type": "Dependency",
+ "admin": "South Georgia and South Sandwich Islands",
+ "adm0_a3": "SGS",
+ "geou_dif": 0,
+ "geounit": "South Georgia and South Sandwich Islands",
+ "gu_a3": "SGS",
+ "su_dif": 0,
+ "subunit": "South Georgia and South Sandwich Islands",
+ "su_a3": "SGS",
+ "brk_diff": 0,
+ "name": "S. Geo. and S. Sandw. Is.",
+ "name_long": "South Georgia and South Sandwich Islands",
+ "brk_a3": "SGS",
+ "brk_name": "S. Geo. and S. Sandw. Is.",
+ "brk_group": null,
+ "abbrev": "S.G. S.S. Is.",
+ "postal": "GS",
+ "formal_en": "South Georgia and South Sandwich Islands",
+ "formal_fr": null,
+ "note_adm0": "U.K.",
+ "note_brk": null,
+ "name_sort": "South Georgia and the Islands",
+ "name_alt": null,
+ "mapcolor7": 6,
+ "mapcolor8": 6,
+ "mapcolor9": 6,
+ "mapcolor13": 3,
+ "pop_est": 30,
+ "gdp_md_est": 0.3,
+ "pop_year": -99,
+ "lastcensus": -99,
+ "gdp_year": -99,
+ "economy": "7. Least developed region",
+ "income_grp": "5. Low income",
+ "wikipedia": -99,
+ "fips_10": null,
+ "iso_a2": "GS",
+ "iso_a3": "SGS",
+ "iso_n3": "239",
+ "un_a3": "-099",
+ "wb_a2": "-99",
+ "wb_a3": "-99",
+ "woe_id": -99,
+ "adm0_a3_is": "SGS",
+ "adm0_a3_us": "SGS",
+ "adm0_a3_un": -99,
+ "adm0_a3_wb": -99,
+ "continent": "Seven seas (open ocean)",
+ "region_un": "Seven seas (open ocean)",
+ "subregion": "Seven seas (open ocean)",
+ "region_wb": "Antarctica",
+ "name_len": 25,
+ "long_len": 40,
+ "abbrev_len": 13,
+ "tiny": 3,
+ "homepart": -99,
+ "featureclass": "Admin-0 Tiny Countries"
+ },
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -36.792143407672654,
+ -54.274478863695265
+ ]
+ }
+ }
+ ]
+} \ 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
new file mode 100644
index 0000000000..e7d140d7d4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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>
+ <string name="menuitem_title_anticipate_overshoot">Anticipate/Overshoot interpolator</string>
+ <string name="menuitem_title_path">PathInterpolator interpolator</string>
+ <string name="menuitem_toggle_symbol_layer_visibility">Toggle Symbol Layer Visibility</string>
+ <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>
+ <string name="action_remove_polylines">Remove polylines</string>
+ <string name="action_visibility_polygon">Change visibility</string>
+ <string name="action_alpha_polygon">Change alpha</string>
+ <string name="action_points_polygon">Change points</string>
+ <string name="action_color_polygon">Change color</string>
+ <string name="action_holes_polygon">Change holes</string>
+ <string name="action_width_polyline">Change width</string>
+ <string name="action_calculate_distance">"Click the map to calculate the distance"</string>
+ <string name="action_scroll_by">Move the map by x/y pixels</string>
+ <string name="navigation_drawer_open">Open navigation drawer</string>
+ <string name="navigation_drawer_close">Close navigation drawer</string>
+ <string name="scrollby_x_value">X: %1$d</string>
+ <string name="scrollby_y_value">Y: %1$d</string>
+ <string name="dialog_camera_position">Animate to new position</string>
+ <string name="dynamic_marker_chelsea_title">Chelsea</string>
+ <string name="dynamic_marker_chelsea_snippet">Stamford Bridge</string>
+ <string name="dynamic_marker_arsenal_title">Arsenal</string>
+ <string name="dynamic_marker_arsenal_snippet">Emirates Stadium</string>
+ <string name="debug_zoom">Zoom: %.2f</string>
+ <string name="viewcache_size">ViewCache size %.2f</string>
+ <string name="latitude">Latitude</string>
+ <string name="min_value">-180</string>
+ <string name="longitude">Longitude</string>
+ <string name="zoom">Zoom</string>
+ <string name="default_zoom_value">18</string>
+ <string name="bearing">Bearing</string>
+ <string name="default_tilt_value">0</string>
+ <string name="tilt">Tilt</string>
+ <string name="no_results">No Results</string>
+ <string name="change_intensity">Change intensity</string>
+ <string name="change_anchor">Change Anchor</string>
+ <string name="amount_of_markers">Amount of markers</string>
+ <string name="update_layer_invalidate">Update layer (invalidate)</string>
+ <string name="red">Red</string>
+ <string name="green">Green</string>
+ <string name="blue">Blue</string>
+ <string name="add_an_exponential_zoom_function">Add an exponential zoom function</string>
+ <string name="add_an_interval_zoom_function">Add an interval zoom function</string>
+ <string name="add_a_categorical_source_function">Add a categorical source function</string>
+ <string name="add_an_exponential_source_function">Add an exponential source function</string>
+ <string name="add_an_identity_source_function">Add an identity source function</string>
+ <string name="add_an_interval_source_function">Add an interval source function</string>
+ <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>
+ <string name="color_the_water">Color the water</string>
+ <string name="set_background_opacity">Set background opacity</string>
+ <string name="set_road_symbol_placement_to_point">Set road symbol placement to Point</string>
+ <string name="set_layer_visibility_to_false">Set layer visibility to false</string>
+ <string name="add_a_parks_layer">Add a parks layer</string>
+ <string name="add_a_dynamic_geojson_source">Add a dynamic GeoJSON source</string>
+ <string name="remove_buildings_layer">Remove buildings layer</string>
+ <string name="add_a_terrain_layer">Add a terrain layer</string>
+ <string name="add_a_satellite_layer">Add a satellite layer</string>
+ <string name="change_the_water_color_on_zoom">Change the water color on zoom</string>
+ <string name="custom_tiles">Custom tiles</string>
+ <string name="apply_filtered_fill">Apply filtered fill</string>
+ <string name="apply_filtered_line">Apply filtered line</string>
+ <string name="apply_numeric_fill_filter">Apply numeric fill filter</string>
+ <string name="toggle_text_size">Toggle text size</string>
+ <string name="toggle_text_field_contents">Toggle text field contents</string>
+ <string name="toggle_text_font">Toggle text font</string>
+ <string name="zoom_in">Zoom in</string>
+ <string name="zoom_out">Zoom out</string>
+ <string name="zoom_by_2">Zoom by 2</string>
+ <string name="zoom_to_point">Zoom to point</string>
+ <string name="zoom_to_4">Zoom to 4</string>
+</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
new file mode 100644
index 0000000000..dbc6b59db6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="category">category</string>
+ <string name="category_basic">_Basic</string>
+ <string name="category_annotation">Annotation</string>
+ <string name="category_camera">Camera</string>
+ <string name="category_custom_layer">Custom Layer</string>
+ <string name="category_fragment">Fragment</string>
+ <string name="category_imagegenerator">Image Generator</string>
+ <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>
+ <string name="category_textureview">Texture View</string>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
new file mode 100644
index 0000000000..568c53201f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -0,0 +1,72 @@
+<?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>
+ <string name="description_map_fragment">Showcase MapFragment</string>
+ <string name="description_map_fragment_support">Showcase SupportMapFragment</string>
+ <string name="description_multimap">Activity with multiple maps on screen</string>
+ <string name="description_press_for_marker">Add marker to map on long press</string>
+ <string name="description_camera_zoom">Different types of zoom methods</string>
+ <string name="description_minmax_zoom">Configure a max and min zoomlevel</string>
+ <string name="description_info_window">Learn how to handle the InfoWindow</string>
+ <string name="description_add_bulk_markers">Add Markers In Bulk to a Map</string>
+ <string name="description_camera_animation_types">Showcase the different animation types</string>
+ <string name="description_visible_bounds">Center the camera around a bounds</string>
+ <string name="description_dynamic_marker">Update position and icon</string>
+ <string name="description_map_padding">Map Padding example</string>
+ <string name="description_debug_mode">Debug Mode</string>
+ <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_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>
+ <string name="description_snapshot">Example to make a snapshot of the map</string>
+ <string name="description_doublemap">2 maps in a view hierarchy</string>
+ <string name="description_view_marker">Use an Android SDK View as marker</string>
+ <string name="description_dynamic_info_window_adapter">Learn how to create a dynamic custom InfoWindow</string>
+ <string name="description_viewpager">Use SupportMapFragments in a ViewPager</string>
+ <string name="description_runtime_style">Adopt the map style on the fly</string>
+ <string name="description_data_driven_style">Use functions to change the map appearance</string>
+ <string name="description_symbol_layer">Manipulate symbols at runtime</string>
+ <string name="description_custom_sprite">Use a custom sprite in a Symbol Layer</string>
+ <string name="description_geojson_clustering">Use GeoJson sources and dynamic layers to cluster information</string>
+ <string name="description_geojson_realtime">Use realtime GeoJSON data streams to move a symbol on your map</string>
+ <string name="description_print">Shows how to print a map</string>
+ <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">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>
+ <string name="description_visibility_map">Changes visibility of map and view parent</string>
+ <string name="description_add_remove_markers">Change Symbol icon when zoom levels changes</string>
+ <string name="description_style_file">Use a local file as the style</string>
+ <string name="description_map_in_dialog">Display a map inside a dialog fragment</string>
+ <string name="description_marker_view_rectangle">Marker Views within a rectangle</string>
+ <string name="description_circle_layer">Show bus stops and route in Singapore</string>
+ <string name="description_url_transform">Transform urls on the fly</string>
+ <string name="description_restricted_bounds">Limit viewport to Iceland</string>
+ <string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string>
+ <string name="description_building_fill_extrusion_layer">Shows how to show 3D extruded buildings</string>
+ <string name="description_animated_image_source">Shows how to animate georeferenced images</string>
+ <string name="description_bottom_sheet">Show 2 MapView on screen with a bottom sheet</string>
+ <string name="description_map_snapshotter">Show a static bitmap taken with the MapSnapshotter</string>
+ <string name="description_map_snapshotter_reuse">Show how to reuse a MapSnapshotter instance</string>
+ <string name="description_map_snapshotter_marker">Show how to add a marker to a Snapshot</string>
+ <string name="description_camera_animator">Use Android SDK Animators to animate camera position changes</string>
+ <string name="description_symbol_generator">Use Android SDK Views as symbols</string>
+ <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_grid_source">Example Custom Geometry Source</string>
+ <string name="description_local_glyph">Suzhou using Droid Sans for Chinese glyphs</string>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
index 402d42d485..0a43af09de 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <dimen name="circle_size">24dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="attr_margin">10dp</dimen>
- <dimen name="coordinatebounds_margin">32dp</dimen>
<dimen name="map_padding_left">96dp</dimen>
<dimen name="map_padding_bottom">256dp</dimen>
<dimen name="map_padding_right">32dp</dimen>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index 0dd0b343fb..15a916fac9 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -1,180 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Mapbox Android SDK TestApp</string>
-
- <!--Activity-->
- <string name="activity_map_fragment_suport">Support Map Fragment</string>
- <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_dynamic_marker">Dynamic Marker</string>
- <string name="activity_polyline">Polyline</string>
- <string name="activity_polygon">Polygon</string>
- <string name="activity_press_for_marker">Press Map For Marker</string>
- <string name="activity_view_marker">View Marker API</string>
- <string name="activity_add_remove_markers">Add/Remove marker</string>
- <string name="activity_info_window">Standard InfoWindow</string>
- <string name="activity_infowindow_adapter">Custom InfoWindow</string>
- <string name="activity_dynamic_infowindow_adapter">Custom Dynamic InfoWindow</string>
- <string name="activity_camera_animation_types">Animation Types</string>
- <string name="activity_camera_zoom">Zoom Methods</string>
- <string name="activity_visible_coordinate_bounds">LatLngBounds Method</string>
- <string name="activity_camera_position">CameraPosition Method</string>
- <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>
- <string name="activity_offline">Offline Map</string>
- <string name="activity_update_metadata">Update metadata Map</string>
- <string name="activity_offline_region_delete">Delete region</string>
- <string name="activity_minmax_zoom">Min/Max Zoom</string>
- <string name="activity_viewpager">ViewPager</string>
- <string name="activity_runtime_style">Runtime Style</string>
- <string name="activity_data_driven_style">Data Driven Style</string>
- <string name="activity_circle_layer">Circle layer</string>
- <string name="activity_style_file">Local Style file</string>
- <string name="activity_geojson_clustering">GeoJson Clustering</string>
- <string name="activity_geojson_realtime">Add live realtime data</string>
- <string name="activity_print">Print a map</string>
- <string name="activity_query_rendered_feature_properties">Query feature properties</string>
- <string name="activity_query_rendered_features_box_count">Count features in box</string>
- <string name="activity_query_rendered_features_box_symbol_count">Count symbols in box</string>
- <string name="activity_query_rendered_features_box_highlight">Highlight features in box</string>
- <string name="activity_query_source_features">Query source features</string>
- <string name="activity_symbol_layer">Symbols</string>
- <string name="activity_add_sprite">Add Custom Sprite</string>
- <string name="activity_navigation_drawer">Android SDK View integration</string>
- <string name="activity_simple_map">Simple Map</string>
- <string name="activity_map_in_dialog">Dialog with map</string>
- <string name="activity_marker_view_rectangle">Marker views in rectangle</string>
- <string name="activity_url_transform">Url transform</string>
- <string name="activity_restricted_bounds">Restrict camera to a bounds</string>
- <string name="activity_fill_extrusion_layer">Fill extrusions</string>
- <string name="activity_building_fill_extrusion_layer">Building layer</string>
- <string name="activity_animated_image_source">Animated Image Source</string>
-
- <!--Description-->
- <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>
- <string name="description_map_fragment">Showcase MapFragment</string>
- <string name="description_map_fragment_support">Showcase SupportMapFragment</string>
- <string name="description_multimap">Activity with multiple maps on screen</string>
- <string name="description_press_for_marker">Add marker to map on long press</string>
- <string name="description_camera_zoom">Different types of zoom methods</string>
- <string name="description_minmax_zoom">Configure a max and min zoomlevel</string>
- <string name="description_info_window">Learn how to handle the InfoWindow</string>
- <string name="description_add_bulk_markers">Add Markers In Bulk to a Map</string>
- <string name="description_camera_animation_types">Showcase the different animation types</string>
- <string name="description_visible_bounds">Center the camera around a bounds</string>
- <string name="description_dynamic_marker">Update position and icon</string>
- <string name="description_map_padding">Map Padding example</string>
- <string name="description_debug_mode">Debug Mode</string>
- <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_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>
- <string name="description_snapshot">Example to make a snapshot of the map</string>
- <string name="description_doublemap">2 maps in a view hierarchy</string>
- <string name="description_view_marker">Use an Android SDK View as marker</string>
- <string name="description_dynamic_info_window_adapter">Learn how to create a dynamic custom InfoWindow</string>
- <string name="description_viewpager">Use SupportMapFragments in a ViewPager</string>
- <string name="description_runtime_style">Adopt the map style on the fly</string>
- <string name="description_data_driven_style">Use functions to change the map appearance</string>
- <string name="description_symbol_layer">Manipulate symbols at runtime</string>
- <string name="description_custom_sprite">Use a custom sprite in a Symbol Layer</string>
- <string name="description_geojson_clustering">Use GeoJson sources and dynamic layers to cluster information</string>
- <string name="description_geojson_realtime">Use realtime GeoJSON data streams to move a symbol on your map</string>
- <string name="description_print">Shows how to print a map</string>
- <string name="description_navigation_drawer">Test animation of Android SDK View components</string>
- <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_source_features">Query source for features</string>
- <string name="description_simple_map">Shows a simple map</string>
- <string name="description_add_remove_markers">Based on zoom level</string>
- <string name="description_style_file">Use a local file as the style</string>
- <string name="description_map_in_dialog">Display a map inside a dialog fragment</string>
- <string name="description_marker_view_rectangle">Marker Views within a rectangle</string>
- <string name="description_circle_layer">Show bus stops and route in Singapore</string>
- <string name="description_url_transform">Transform urls on the fly</string>
- <string name="description_restricted_bounds">Limit viewport to Iceland</string>
- <string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string>
- <string name="description_building_fill_extrusion_layer">Shows how to show 3D extruded buildings</string>
- <string name="description_animated_image_source">Shows how to animate georeferenced images</string>
-
- <!--Categories-->
- <string name="category">category</string>
- <string name="category_basic">_Basic</string>
- <string name="category_annotation">Annotation</string>
- <string name="category_camera">Camera</string>
- <string name="category_custom_layer">Custom Layer</string>
- <string name="category_fragment">Fragment</string>
- <string name="category_imagegenerator">Image Generator</string>
- <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>
-
- <!--Actions-->
- <string name="action_remove_polylines">Remove polylines</string>
- <string name="action_visibility_polygon">Change visibility</string>
- <string name="action_alpha_polygon">Change alpha</string>
- <string name="action_points_polygon">Change points</string>
- <string name="action_color_polygon">Change color</string>
- <string name="action_holes_polygon">Change holes</string>
- <string name="action_width_polyline">Change width</string>
-
- <!--Menu-->
- <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>
-
- <!--Button-->
- <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>
-
- <!--Other-->
- <string name="navigation_drawer_open">Open navigation drawer</string>
- <string name="navigation_drawer_close">Close navigation drawer</string>
- <string name="scrollby_x_value">X: %1$d</string>
- <string name="scrollby_y_value">Y: %1$d</string>
- <string name="dialog_camera_position">Animate to new position</string>
- <string name="dynamic_marker_chelsea_title">Chelsea</string>
- <string name="dynamic_marker_chelsea_snippet">Stamford Bridge</string>
- <string name="dynamic_marker_arsenal_title">Arsenal</string>
- <string name="dynamic_marker_arsenal_snippet">Emirates Stadium</string>
- <string name="debug_zoom">Zoom: %s</string>
</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
new file mode 100644
index 0000000000..c4d13e1068
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="activity_map_fragment_suport">Support Map Fragment</string>
+ <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_dynamic_marker">Dynamic Marker</string>
+ <string name="activity_polyline">Polyline</string>
+ <string name="activity_polygon">Polygon</string>
+ <string name="activity_press_for_marker">Press Map For Marker</string>
+ <string name="activity_view_marker">View Marker API</string>
+ <string name="activity_add_remove_markers">Zoom function with SymbolLayer</string>
+ <string name="activity_info_window">Standard InfoWindow</string>
+ <string name="activity_infowindow_adapter">Custom InfoWindow</string>
+ <string name="activity_dynamic_infowindow_adapter">Custom Dynamic InfoWindow</string>
+ <string name="activity_camera_animation_types">Animation Types</string>
+ <string name="activity_camera_zoom">Zoom Methods</string>
+ <string name="activity_visible_coordinate_bounds">LatLngBounds Method</string>
+ <string name="activity_camera_position">CameraPosition Method</string>
+ <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>
+ <string name="activity_offline">Offline Map</string>
+ <string name="activity_update_metadata">Update metadata Map</string>
+ <string name="activity_offline_region_delete">Delete region</string>
+ <string name="activity_minmax_zoom">Min/Max Zoom</string>
+ <string name="activity_viewpager">ViewPager</string>
+ <string name="activity_runtime_style">Runtime Style</string>
+ <string name="activity_data_driven_style">Data Driven Style</string>
+ <string name="activity_circle_layer">Circle layer</string>
+ <string name="activity_style_file">Local Style file</string>
+ <string name="activity_geojson_clustering">GeoJson Clustering</string>
+ <string name="activity_geojson_realtime">Add live realtime data</string>
+ <string name="activity_print">Print a map</string>
+ <string name="activity_query_rendered_feature_properties">Query feature properties</string>
+ <string name="activity_query_rendered_features_box_count">Count features in box</string>
+ <string name="activity_query_rendered_features_box_symbol_count">Count symbols in box</string>
+ <string name="activity_query_rendered_features_box_highlight">Highlight features in box</string>
+ <string name="activity_query_source_features">Query source features</string>
+ <string name="activity_symbol_layer">Symbols</string>
+ <string name="activity_add_sprite">Add Custom Sprite</string>
+ <string name="activity_simple_map">Simple Map</string>
+ <string name="activity_map_change">Map Change Events</string>
+ <string name="activity_map_visibility">Visibility Map</string>
+ <string name="activity_map_in_dialog">Dialog with map</string>
+ <string name="activity_marker_view_rectangle">Marker views in rectangle</string>
+ <string name="activity_url_transform">Url transform</string>
+ <string name="activity_restricted_bounds">Restrict camera to a bounds</string>
+ <string name="activity_fill_extrusion_layer">Fill extrusions</string>
+ <string name="activity_building_fill_extrusion_layer">Building layer</string>
+ <string name="activity_animated_image_source">Animated Image Source</string>
+ <string name="activity_bottom_sheet">Bottom sheet</string>
+ <string name="activity_map_snapshotter">Map Snapshotter</string>
+ <string name="activity_map_snapshotter_reuse">Map Snapshotter Reuse</string>
+ <string name="activity_map_snapshotter_marker">Map Snapshot with marker</string>
+ <string name="activity_camera_animator">Animator animation</string>
+ <string name="activity_symbol_generator">SymbolGenerator</string>
+ <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_grid_source">Grid Source</string>
+ <string name="activity_local_glyph">Local CJK glyph generation</string>
+</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKWearTestApp/build.gradle
deleted file mode 100644
index 6ac8961421..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/build.gradle
+++ /dev/null
@@ -1,50 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion rootProject.ext.compileSdkVersion
- buildToolsVersion rootProject.ext.buildToolsVersion
-
- defaultConfig {
- applicationId "com.mapbox.mapboxsdk.testapp"
- minSdkVersion rootProject.ext.minSdkVersion
- targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode rootProject.ext.versionCode
- versionName rootProject.ext.versionName
- }
-
- lintOptions {
- disable 'MissingTranslation'
- }
-
- buildTypes {
- debug {
- testCoverageEnabled = true
- }
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
- compile(project(':MapboxGLAndroidSDK')) {
- transitive = true
- }
-
- // Wear
- compile rootProject.ext.dep.wearCompile
- provided rootProject.ext.dep.wearProvided
-
- // Leak Canary
- debugCompile rootProject.ext.dep.leakCanaryDebug
- releaseCompile rootProject.ext.dep.leakCanaryRelease
- testCompile rootProject.ext.dep.leakCanaryTest
-
- // Testing dependencies
- testCompile rootProject.ext.dep.junit
- testCompile rootProject.ext.dep.mockito
-}
-
-apply from: 'gradle-config.gradle'
-apply from: 'gradle-checkstyle.gradle'
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-checkstyle.gradle b/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-checkstyle.gradle
deleted file mode 100644
index bfb8341dbc..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-checkstyle.gradle
+++ /dev/null
@@ -1,17 +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/**'
- classpath = files()
- ignoreFailures = false
-}
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-config.gradle b/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-config.gradle
deleted file mode 100644
index 27c13b935b..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-config.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Configuration file for gradle build execution.
-//
-
-task accessToken {
- def tokenFile = new File("MapboxGLAndroidSDKWearTestApp/src/main/res/values/developer-config.xml")
- if (!tokenFile.exists()) {
- String tokenFileContents = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
- "<resources>\n" +
- " <string name=\"mapbox_access_token\">" + "$System.env.MAPBOX_ACCESS_TOKEN" + "</string>\n" +
- "</resources>"
-
- if (tokenFileContents == null) {
- throw new InvalidUserDataException("You must set the MAPBOX_ACCESS_TOKEN environment variable.")
- }
- tokenFile.write(tokenFileContents)
- }
-}
-
-gradle.projectsEvaluated {
- preBuild.dependsOn('accessToken')
-}
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/proguard-rules.pro b/platform/android/MapboxGLAndroidSDKWearTestApp/proguard-rules.pro
deleted file mode 100644
index 362685b172..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/proguard-rules.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in /Users/cameron/Library/Android/sdk/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# Add any project specific keep options here:
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/AndroidManifest.xml
deleted file mode 100644
index 36588a89f5..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.mapbox.weartestapp">
-
- <uses-feature android:name="android.hardware.type.watch"/>
-
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
- <application
- android:name=".MapboxApplication"
- android:allowBackup="true"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:supportsRtl="true"
- android:theme="@android:style/Theme.DeviceDefault">
- <uses-library
- android:name="com.google.android.wearable"
- android:required="false"/>
-
- <activity
- android:name="com.mapbox.weartestapp.activity.FeatureOverviewActivity"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
-
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity
- android:name=".activity.SimpleWearMapActivity"
- android:label="@string/activity_simple_mapview_title">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
- </activity>
-
- <service android:name="com.mapbox.services.android.telemetry.service.TelemetryService"/>
-
- </application>
-
-</manifest>
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java
deleted file mode 100644
index cbbdcb8493..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.mapbox.weartestapp;
-
-import android.app.Application;
-import android.os.StrictMode;
-
-import com.mapbox.mapboxsdk.Mapbox;
-import com.squareup.leakcanary.LeakCanary;
-
-public class MapboxApplication extends Application {
-
- @Override
- public void onCreate() {
- super.onCreate();
- Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token));
- LeakCanary.install(this);
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectDiskReads()
- .detectDiskWrites()
- .detectNetwork() // or .detectAll() for all detectable problems
- .penaltyLog()
- .build());
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectLeakedSqlLiteObjects()
- .penaltyLog()
- .penaltyDeath()
- .build());
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/FeatureOverviewActivity.java
deleted file mode 100644
index 1fe8a6cf10..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/FeatureOverviewActivity.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.mapbox.weartestapp.activity;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.wearable.activity.WearableActivity;
-import android.support.wearable.view.WearableRecyclerView;
-
-import com.mapbox.weartestapp.R;
-import com.mapbox.weartestapp.adapter.FeatureAdapter;
-import com.mapbox.weartestapp.model.Feature;
-import com.mapbox.weartestapp.utils.OffsettingHelper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class FeatureOverviewActivity extends WearableActivity implements FeatureAdapter.ItemSelectedListener {
-
- private WearableRecyclerView wearableRecyclerView;
- private List<Feature> exampleItemModels;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_feature_overview);
-
- wearableRecyclerView = (WearableRecyclerView) findViewById(R.id.recycler_launcher_view);
- wearableRecyclerView.setHasFixedSize(true);
-
- OffsettingHelper offsettingHelper = new OffsettingHelper();
-
- wearableRecyclerView.setOffsettingHelper(offsettingHelper);
-
- exampleItemModels = new ArrayList<>();
- exampleItemModels.add(new Feature(R.string.activity_simple_mapview_title, new Intent(FeatureOverviewActivity.this,
- SimpleWearMapActivity.class)));
-
- FeatureAdapter exampleAdapter = new FeatureAdapter(FeatureOverviewActivity.this, exampleItemModels);
- wearableRecyclerView.setAdapter(exampleAdapter);
-
- exampleAdapter.setListener(this);
- }
-
- @Override
- public void onItemSelected(int position) {
- startActivity(exampleItemModels.get(position).getActivity());
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/adapter/FeatureAdapter.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/adapter/FeatureAdapter.java
deleted file mode 100644
index 1ef17e2d7a..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/adapter/FeatureAdapter.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package com.mapbox.weartestapp.adapter;
-
-import android.content.Context;
-import android.support.v7.widget.RecyclerView;
-import android.support.wearable.view.WearableRecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.mapbox.weartestapp.R;
-import com.mapbox.weartestapp.model.Feature;
-
-import java.util.List;
-
-public class FeatureAdapter extends WearableRecyclerView.Adapter<FeatureAdapter.ViewHolder> {
-
- private List<Feature> data;
- private Context context;
- private ItemSelectedListener itemSelectedListener;
-
- public FeatureAdapter(Context context, List<Feature> data) {
- this.context = context;
- this.data = data;
- }
-
- static class ViewHolder extends RecyclerView.ViewHolder {
-
- private TextView textView;
-
- ViewHolder(View view) {
- super(view);
- textView = (TextView) view.findViewById(R.id.text_item);
- }
-
- void bind(final int position, final ItemSelectedListener listener) {
-
- itemView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (listener != null) {
- listener.onItemSelected(position);
- }
- }
- });
- }
- }
-
- public void setListener(ItemSelectedListener itemSelectedListener) {
- this.itemSelectedListener = itemSelectedListener;
- }
-
- @Override
- public FeatureAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new ViewHolder(LayoutInflater.from(parent.getContext())
- .inflate(R.layout.item_curved_layout, parent, false));
- }
-
- @Override
- public void onBindViewHolder(FeatureAdapter.ViewHolder holder, final int position) {
- if (data != null && !data.isEmpty()) {
- holder.textView.setText(data.get(position).getTitle());
- holder.bind(position, itemSelectedListener);
- }
- }
-
- @Override
- public int getItemCount() {
- if (data != null && !data.isEmpty()) {
- return data.size();
- }
- return 0;
- }
-
- public interface ItemSelectedListener {
- void onItemSelected(int position);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/model/Feature.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/model/Feature.java
deleted file mode 100644
index 65954ec27e..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/model/Feature.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.mapbox.weartestapp.model;
-
-import android.content.Intent;
-
-public class Feature {
-
- public int title;
- public Intent activity;
-
- public int getTitle() {
- return title;
- }
-
- public void setTitle(int title) {
- this.title = title;
- }
-
- public Intent getActivity() {
- return activity;
- }
-
- public void setActivity(Intent activity) {
- this.activity = activity;
- }
-
- public Feature(int title, Intent activity) {
- this.title = title;
- this.activity = activity;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/utils/OffsettingHelper.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/utils/OffsettingHelper.java
deleted file mode 100644
index 8550d0d016..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/utils/OffsettingHelper.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.mapbox.weartestapp.utils;
-
-import android.support.wearable.view.DefaultOffsettingHelper;
-import android.support.wearable.view.WearableRecyclerView;
-import android.view.View;
-
-public class OffsettingHelper extends DefaultOffsettingHelper {
-
- /**
- * How much should we scale the icon at most.
- */
- private static final float MAX_ICON_PROGRESS = 0.65f;
-
- private float progressToCenter;
-
- public OffsettingHelper() {
- }
-
- @Override
- public void updateChild(View child, WearableRecyclerView parent) {
- super.updateChild(child, parent);
-
- // Figure out % progress from top to bottom
- float centerOffset = ((float) child.getHeight() / 2.0f) / (float) parent.getHeight();
- float yRelativeToCenterOffset = (child.getY() / parent.getHeight()) + centerOffset;
-
- // Normalize for center
- progressToCenter = Math.abs(0.5f - yRelativeToCenterOffset);
- // Adjust to the maximum scale
- progressToCenter = Math.min(progressToCenter, MAX_ICON_PROGRESS);
-
- child.setScaleX(1 - progressToCenter);
- child.setScaleY(1 - progressToCenter);
- }
-
- @Override
- public void adjustAnchorOffsetXY(View child, float[] anchorOffsetXY) {
- anchorOffsetXY[0] = child.getHeight() / 2.0f;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_feature_overview.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_feature_overview.xml
deleted file mode 100644
index d1a314cfe2..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_feature_overview.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.wearable.view.BoxInsetLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".activity.FeatureOverviewActivity"
- tools:deviceIds="wear">
-
- <android.support.wearable.view.WearableRecyclerView
- android:id="@+id/recycler_launcher_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scrollbars="vertical" />
-
-</android.support.wearable.view.BoxInsetLayout>
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml
deleted file mode 100644
index 44374f2c6c..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:mapbox="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".activity.SimpleWearMapActivity"
- tools:deviceIds="wear">
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- mapbox:mapbox_cameraTargetLat="40.73581"
- mapbox:mapbox_cameraTargetLng="-73.99155"
- mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v10"
- mapbox:mapbox_cameraZoom="11"
- mapbox:mapbox_uiZoomControls="false"/>
-
-</FrameLayout>
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/item_curved_layout.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/item_curved_layout.xml
deleted file mode 100644
index 3d81ba3ad5..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/item_curved_layout.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/layout"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="10dp"
- android:clickable="true"
- android:orientation="horizontal">
-
- <TextView
- android:id="@+id/text_item"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:textColor="@color/mapboxWhite"
- android:textSize="14sp"/>
-
-</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-hdpi/ic_launcher.png b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index ac2ea61c73..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-mdpi/ic_launcher.png b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 99eed7146c..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xhdpi/ic_launcher.png b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 9b084daf91..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xxhdpi/ic_launcher.png b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 6fa714b47d..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/colors.xml
deleted file mode 100644
index 5bcdbe93bf..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <!-- Mapbox colors -->
- <color name="colorPrimary">@color/mapboxBlue</color>
- <color name="colorPrimaryDark">@color/mapboxBlueDark</color>
- <color name="colorAccent">@color/mapboxRed</color>
-
- <color name="materialGrey">#F5F5F5</color>
- <color name="materialDarkGrey">#DFDFDF</color>
-
- <color name="mapboxWhite">#ffffff</color>
- <color name="mapboxCyan">#3BB2D0</color>
- <color name="mapboxGreen">#56B881</color>
- <color name="mapboxBlue">#3887BE</color>
- <color name="mapboxBlueDark">#1F6EA5</color>
- <color name="mapboxPurple">#8A8ACB</color>
- <color name="mapboxPurpleDark">#7171b2</color>
- <color name="mapboxPurpleLight">#A4A4E5</color>
-
- <color name="mapboxDenim">#50667F</color>
- <color name="mapboxTeal">#41AFA5</color>
- <color name="mapboxOrange">#F9886C</color>
- <color name="mapboxRed">#E55E5E</color>
- <color name="mapboxPink">#ED6498</color>
- <color name="mapboxYellow">#f1f075</color>
- <color name="mapboxMustard">#FBB03B</color>
- <color name="mapboxNavy">#28353D</color>
- <color name="mapboxNavyDark">#222B30</color>
-
-</resources>
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/strings.xml
deleted file mode 100644
index e6a10ad308..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<resources>
- <string name="app_name">MapboxGLAndroidSDKWearTestApp</string>
-
- <!-- Feature Titles -->
- <string name="activity_simple_mapview_title">A simple map view</string>
-</resources>
diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/test/java/com/mapbox/weartestapp/utils/OffsettingHelperTest.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/test/java/com/mapbox/weartestapp/utils/OffsettingHelperTest.java
deleted file mode 100644
index aab7714947..0000000000
--- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/test/java/com/mapbox/weartestapp/utils/OffsettingHelperTest.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.mapbox.weartestapp.utils;
-
-import android.view.View;
-
-import org.junit.Test;
-import org.mockito.InjectMocks;
-
-import static junit.framework.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class OffsettingHelperTest {
-
- private static final double DELTA = 1e-15;
-
- @InjectMocks
- View view = mock(View.class);
-
- @Test
- public void testAnchorOffset() {
- float[] offset = new float[2];
- int viewHeight = 50;
- when(view.getHeight()).thenReturn(viewHeight);
- OffsettingHelper offsettingHelper = new OffsettingHelper();
- offsettingHelper.adjustAnchorOffsetXY(view, offset);
- assertEquals("Offset of " + viewHeight + " should be divided by 2: ", viewHeight / 2, offset[0], DELTA);
- }
-}
diff --git a/platform/android/README.md b/platform/android/README.md
index ac4eafc218..5309d5f19d 100644
--- a/platform/android/README.md
+++ b/platform/android/README.md
@@ -1,16 +1,18 @@
-# [Mapbox Android SDK](https://www.mapbox.com/android-sdk/)
+# [Mapbox Maps SDK for Android](https://www.mapbox.com/android-sdk/)
-[![Bitrise](https://www.bitrise.io/app/79cdcbdc42de4303.svg?token=_InPF8bII6W7J6kFr-L8QQ&branch=master)](https://www.bitrise.io/app/79cdcbdc42de4303)
+[![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.
-**To view our current API documentation, see our [JavaDoc](https://www.mapbox.com/android-docs/api/map-sdk/5.0.2/index.html).**
+**To view our current API documentation for the Maps SDK for Android, please see our [JavaDoc](https://www.mapbox.com/android-docs/api/map-sdk/5.0.2/index.html).**
-**To install and use the Mapbox Android SDK in an application, see the [Mapbox Android Maps SDK Overview](https://www.mapbox.com/android-docs/map-sdk/overview/).**
+**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/)
### Setup environment
@@ -21,67 +23,92 @@ Alright. So, actually, you may be in the wrong place. From here on in, this READ
Clone the git repository
```bash
-git clone https://github.com/mapbox/mapbox-gl-native.git
-cd mapbox-gl-native
+git clone git@github.com:mapbox/mapbox-gl-native.git && cd mapbox-gl-native
```
#### Installing dependencies
These dependencies are required for all operating systems and all platform targets.
-- Latest stable [Android Studio](https://developer.android.com/studio/index.html)
-- Update Android SDK with latest
- - Android SDK Build-Tools
+- Latest stable [Android Studio](https://developer.android.com/studio/index.html)
+- Update the Mapbox Maps SDK for Android with the latest
+ - Android SDK Build-Tools
- Android Platform-Tools
- Android SDK Tools
- CMake
- NDK
- LLDB
-
-- Modern C++ compiler that supports -std=c++14
+- Modern C++ compiler that supports `-std=c++14`\*
- clang++ 3.5 or later or
- - g++-5 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)
+ - g++-4.9 or later
+- [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).
+
+**Note**: On macOS you can install clang with installing the [Apple command line developer tools](https://developer.apple.com/download/).
+
+### Opening the project
-##### Additional Dependencies for Linux
+#### macOS
-_These instructions were tested on Ubuntu 16.04 LTS (aka Xenial Xerus)._
+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.
+### Running project
-##### Setting Mapbox Access Token
+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`.
-_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/)._
+More information about building and distributing this project in [DISTRIBUTE.md](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/DISTRIBUTE.md).
+
+### Additional resources
+
+#### Using the SDK snapshot
-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.
+Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number. For example `5.2.0-SNAPSHOT` or:
+
+```java
+// Mapbox SDK dependency
+compile('com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT@aar') {
+ transitive = true
+}
+```
+You need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies:
+```
+allprojects {
+ repositories {
+ jcenter()
+ maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
+ }
+}
+```
-#### Running project
-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`.
+#### Symbolicating native crashes
-More information about building and distributing this project in [DISTRIBUTE.md][https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/DISTRIBUTE.md].
+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 bc90896812..28d5363797 100644
--- a/platform/android/build.gradle
+++ b/platform/android/build.gradle
@@ -1,17 +1,17 @@
buildscript {
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.1'
- classpath 'com.amazonaws:aws-devicefarm-gradle-plugin:1.2'
- classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.1'
+ classpath 'com.android.tools.build:gradle:3.0.1'
}
}
allprojects {
repositories {
jcenter()
+ google()
maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
}
}
diff --git a/platform/android/config.cmake b/platform/android/config.cmake
index a7370da5fd..f5de7a6052 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)
@@ -31,30 +36,14 @@ macro(mbgl_platform_core)
PRIVATE platform/android/src/run_loop_impl.hpp
PRIVATE platform/android/src/timer.cpp
- # File source
- PRIVATE platform/android/src/http_file_source.cpp
- PRIVATE platform/android/src/asset_manager.hpp
- PRIVATE platform/android/src/asset_manager_file_source.cpp
- PRIVATE platform/android/src/asset_manager_file_source.hpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
- PRIVATE platform/default/sqlite3.hpp
-
# Misc
+ PRIVATE platform/android/src/text/local_glyph_rasterizer_jni.hpp
+ PRIVATE platform/android/src/text/local_glyph_rasterizer.cpp
PRIVATE platform/android/src/logging_android.cpp
PRIVATE platform/android/src/thread.cpp
PRIVATE platform/default/string_stdlib.cpp
PRIVATE platform/default/bidi.cpp
+ PRIVATE platform/default/thread_local.cpp
PRIVATE platform/default/utf.cpp
# Image handling
@@ -70,25 +59,34 @@ macro(mbgl_platform_core)
PRIVATE platform/default/mbgl/util/shared_thread_pool.hpp
PRIVATE platform/default/mbgl/util/default_thread_pool.cpp
PRIVATE platform/default/mbgl/util/default_thread_pool.hpp
+
+ # Rendering
+ PRIVATE platform/android/src/android_renderer_backend.cpp
+ PRIVATE platform/android/src/android_renderer_backend.hpp
+ PRIVATE platform/android/src/android_renderer_frontend.cpp
+ PRIVATE platform/android/src/android_renderer_frontend.hpp
+
+ # Snapshots (core)
+ PRIVATE platform/default/mbgl/gl/headless_backend.cpp
+ PRIVATE platform/default/mbgl/gl/headless_backend.hpp
+ PRIVATE platform/default/mbgl/gl/headless_frontend.cpp
+ PRIVATE platform/default/mbgl/gl/headless_frontend.hpp
+ PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
+ PRIVATE platform/default/mbgl/map/map_snapshotter.hpp
+ PRIVATE platform/linux/src/headless_backend_egl.cpp
)
target_include_directories(mbgl-core
PUBLIC platform/default
+ PRIVATE platform/android
)
- target_add_mason_package(mbgl-core PUBLIC sqlite)
target_add_mason_package(mbgl-core PUBLIC nunicode)
target_add_mason_package(mbgl-core PUBLIC geojson)
target_add_mason_package(mbgl-core PUBLIC jni.hpp)
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
@@ -101,6 +99,31 @@ macro(mbgl_platform_core)
)
endmacro()
+
+macro(mbgl_filesource)
+ target_sources(mbgl-filesource
+ # File source
+ PRIVATE platform/android/src/http_file_source.cpp
+ PRIVATE platform/android/src/asset_manager.hpp
+ PRIVATE platform/android/src/asset_manager_file_source.cpp
+ PRIVATE platform/android/src/asset_manager_file_source.hpp
+
+ # Database
+ PRIVATE platform/default/sqlite3.cpp
+ )
+
+ target_add_mason_package(mbgl-filesource PUBLIC sqlite)
+ target_add_mason_package(mbgl-filesource PUBLIC jni.hpp)
+
+ target_link_libraries(mbgl-filesource
+ PUBLIC -llog
+ PUBLIC -landroid
+ PUBLIC -lstdc++
+ PUBLIC -latomic
+ )
+endmacro()
+
+
## Main library ##
add_library(mbgl-android STATIC
@@ -113,10 +136,11 @@ add_library(mbgl-android STATIC
platform/android/src/style/conversion/types_string_values.hpp
platform/android/src/map/camera_position.cpp
platform/android/src/map/camera_position.hpp
+ platform/android/src/map/image.cpp
+ platform/android/src/map/image.hpp
# 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
@@ -148,10 +172,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
@@ -186,6 +210,10 @@ add_library(mbgl-android STATIC
# Native map
platform/android/src/native_map_view.cpp
platform/android/src/native_map_view.hpp
+ platform/android/src/map_renderer.cpp
+ platform/android/src/map_renderer.hpp
+ platform/android/src/map_renderer_runnable.cpp
+ platform/android/src/map_renderer_runnable.hpp
# Java core classes
platform/android/src/java/util.cpp
@@ -259,6 +287,12 @@ add_library(mbgl-android STATIC
platform/android/src/offline/offline_region_status.cpp
platform/android/src/offline/offline_region_status.hpp
+ # Snapshots (SDK)
+ platform/android/src/snapshotter/map_snapshotter.cpp
+ platform/android/src/snapshotter/map_snapshotter.hpp
+ platform/android/src/snapshotter/map_snapshot.cpp
+ platform/android/src/snapshotter/map_snapshot.hpp
+
# Main jni bindings
platform/android/src/attach_env.cpp
platform/android/src/attach_env.hpp
@@ -270,13 +304,8 @@ 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
)
@@ -288,8 +317,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 ##
@@ -301,25 +328,14 @@ macro(mbgl_platform_test)
# Main test entry point
platform/android/src/test/main.jni.cpp
-
- # Headless view
- platform/default/mbgl/gl/headless_backend.cpp
- platform/default/mbgl/gl/headless_backend.hpp
- platform/default/mbgl/gl/offscreen_view.cpp
- platform/default/mbgl/gl/offscreen_view.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()
@@ -329,14 +345,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
index 5e80e344e4..9a8115bf0f 100644
--- a/platform/android/dependencies.gradle
+++ b/platform/android/dependencies.gradle
@@ -2,17 +2,16 @@ ext {
minSdkVersion = 15
targetSdkVersion = 25
compileSdkVersion = 25
- buildToolsVersion = "25.0.2"
+ buildToolsVersion = "26.0.3"
- versionCode = 11
- versionName = "5.0.0"
+ versionCode = 12
+ versionName = "6.0.0"
- mapboxServicesVersion = "2.1.1"
- supportLibVersion = "25.3.1"
- wearableVersion = '2.0.0'
- espressoVersion = '2.2.2'
- testRunnerVersion = '0.5'
- leakCanaryVersion = '1.5'
+ mapboxServicesVersion = "2.2.8"
+ supportLibVersion = "25.4.0"
+ espressoVersion = '3.0.1'
+ testRunnerVersion = '1.0.1'
+ leakCanaryVersion = '1.5.1'
dep = [
// mapbox
@@ -21,14 +20,13 @@ ext {
mapboxAndroidTelemetry : "com.mapbox.mapboxsdk:mapbox-android-telemetry:${mapboxServicesVersion}@aar",
// mapzen lost
- lost : 'com.mapzen.android:lost:3.0.0',
+ lost : 'com.mapzen.android:lost:1.1.1',
// unit test
junit : 'junit:junit:4.12',
- mockito : 'org.mockito:mockito-core:2.2.27',
+ mockito : 'org.mockito:mockito-core:2.10.0',
// instrumentation test
- testSpoonRunner : 'com.squareup.spoon:spoon-client:1.6.2',
testRunner : "com.android.support.test:runner:${testRunnerVersion}",
testRules : "com.android.support.test:rules:${testRunnerVersion}",
testEspressoCore : "com.android.support.test.espresso:espresso-core:${espressoVersion}",
@@ -37,19 +35,14 @@ ext {
// support
supportAnnotations : "com.android.support:support-annotations:${supportLibVersion}",
supportAppcompatV7 : "com.android.support:appcompat-v7:${supportLibVersion}",
- supportV4 : "com.android.support:support-v4:${supportLibVersion}",
+ supportFragmentV4 : "com.android.support:support-fragment:${supportLibVersion}",
supportDesign : "com.android.support:design:${supportLibVersion}",
supportRecyclerView : "com.android.support:recyclerview-v7:${supportLibVersion}",
- // wear
- wearCompile : "com.google.android.support:wearable:${wearableVersion}",
- wearProvided : "com.google.android.wearable:wearable:${wearableVersion}",
-
// square crew
timber : 'com.jakewharton.timber:timber:4.5.1',
- okhttp3 : 'com.squareup.okhttp3:okhttp:3.7.0',
+ okhttp3 : 'com.squareup.okhttp3:okhttp:3.9.0',
leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}",
- leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}",
- leakCanaryTest : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}"
+ leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}"
]
} \ No newline at end of file
diff --git a/platform/android/gradle-lint.gradle b/platform/android/gradle-lint.gradle
new file mode 100644
index 0000000000..cbebeaa74a
--- /dev/null
+++ b/platform/android/gradle-lint.gradle
@@ -0,0 +1,30 @@
+task ciLint(type: Copy) {
+ if (isLocalBuild()) {
+ from "${projectDir}/lint/lint-baseline-local.xml"
+ into "${projectDir}"
+ rename { String fileName ->
+ fileName.replace("lint-baseline-local.xml", "lint-baseline.xml")
+ }
+ } else {
+ from "${projectDir}/lint/lint-baseline-ci.xml"
+ into "${projectDir}"
+ rename { String fileName ->
+ fileName.replace("lint-baseline-ci.xml", "lint-baseline.xml")
+ }
+ }
+}
+
+def isLocalBuild() {
+ if (System.getenv('IS_LOCAL_DEVELOPMENT') != null) {
+ return System.getenv('IS_LOCAL_DEVELOPMENT').toBoolean()
+ }
+ return true
+}
+
+lint.dependsOn ciLint
+
+tasks.whenTaskAdded { task ->
+ if (task.name == 'lintVitalRelease') {
+ task.dependsOn ciLint
+ }
+} \ No newline at end of file
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
new file mode 100644
index 0000000000..f05001c6ae
--- /dev/null
+++ b/platform/android/scripts/exclude-activity-gen.json
@@ -0,0 +1,31 @@
+[
+ "BaseLocationActivity",
+ "MapSnapshotterMarkerActivity",
+ "MapSnapshotterReuseActivity",
+ "LatLngBoundsActivity",
+ "BottomSheetActivity",
+ "MapSnapshotterActivity",
+ "MockLocationEngine",
+ "DeleteRegionActivity",
+ "RealTimeGeoJsonActivity",
+ "UpdateMetadataActivity",
+ "CarDrivingActivity",
+ "MyLocationTrackingModeActivity",
+ "MyLocationToggleActivity",
+ "MyLocationTintActivity",
+ "MyLocationDrawableActivity",
+ "DoubleMapActivity",
+ "LocationPickerActivity",
+ "GeoJsonClusteringActivity",
+ "RuntimeStyleTestActivity",
+ "AnimatedMarkerActivity",
+ "ViewPagerActivity",
+ "MapFragmentActivity",
+ "SupportMapFragmentActivity",
+ "SnapshotActivity",
+ "NavigationDrawerActivity",
+ "QueryRenderedFeaturesBoxHighlightActivity",
+ "MultiMapActivity",
+ "MapInDialogActivity",
+ "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 bb04e3ba2a..abc0796bc1 100644
--- a/platform/android/scripts/generate-style-code.js
+++ b/platform/android/scripts/generate-style-code.js
@@ -111,6 +111,9 @@ global.propertyNativeType = function (property) {
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
+ if (/^(text|icon)-anchor$/.test(property.name)) {
+ return 'SymbolAnchorType';
+ }
switch (property.type) {
case 'boolean':
return 'bool';
@@ -167,11 +170,11 @@ global.defaultValueJava = function(property) {
case 'string':
return '[' + property['default'] + "]";
case 'number':
- var result ='new Float[]{';
+ var result ='new Float[] {';
for (var i = 0; i < property.length; i++) {
result += "0f";
if (i +1 != property.length) {
- result += ",";
+ result += ", ";
}
}
return result + "}";
@@ -267,6 +270,9 @@ global.evaluatedType = function (property) {
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
+ if (/^(text|icon)-anchor$/.test(property.name)) {
+ return 'SymbolAnchorType';
+ }
if (/position/.test(property.name)) {
return 'Position';
}
diff --git a/platform/android/scripts/generate-test-code.js b/platform/android/scripts/generate-test-code.js
index b054e4a2e7..e27de7e40b 100644
--- a/platform/android/scripts/generate-test-code.js
+++ b/platform/android/scripts/generate-test-code.js
@@ -13,8 +13,7 @@ global.camelize = function (str) {
});
}
-
-const excludeActivities = ["DeleteRegionActivity","RealTimeGeoJsonActivity","UpdateMetadataActivity","CarDrivingActivity","MyLocationTrackingModeActivity","MyLocationToggleActivity","MyLocationTintActivity","MyLocationDrawableActivity","DoubleMapActivity", "LocationPickerActivity","GeoJsonClusteringActivity","RuntimeStyleTestActivity", "AnimatedMarkerActivity", "ViewPagerActivity","MapFragmentActivity","SupportMapFragmentActivity","SnapshotActivity","NavigationDrawerActivity", "QueryRenderedFeaturesBoxHighlightActivity", "MultiMapActivity", "MapInDialogActivity", "SimpleMapActivity"];
+const excludeClasses = JSON.parse(fs.readFileSync('platform/android/scripts/exclude-activity-gen.json', 'utf8'));
const appBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity';
const testBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/gen';
const subPackages = fs.readdirSync(appBasePath);
@@ -24,7 +23,9 @@ if (!fs.existsSync(testBasePath)){
fs.mkdirSync(testBasePath);
}
-console.log("Generating test activities:");
+console.log("\nGenerating test activities:\n");
+var generatedClasses = [];
+var excludedClasses = [];
for(const subPackage of subPackages) {
if(!(subPackage.slice(-5) == '.java')) {
const activities = fs.readdirSync(appBasePath+'/'+subPackage);
@@ -45,18 +46,22 @@ for(const subPackage of subPackages) {
try {
fs.accessSync(filePath, fs.F_OK);
fs.unlinkSync(filePath);
- console.log("Removed file: "+filePath);
} catch (e) {
- console.log("No file found: "+filePath);
}
- // only generate test file if not part of exclude list
- if (!(excludeActivities.indexOf(activityName) > -1)) {
- console.log("Created file: "+filePath);
+ // only generate test file if not part of exclude list + if contains Activity in name
+ if ((!(excludeClasses.indexOf(activityName) > -1)) && activityName.includes("Activity")) {
fs.writeFileSync(filePath, ejsConversionTask([activityName, subPackage]));
+ generatedClasses.push(activityName);
}else{
- console.log("Excluding file: "+filePath);
+ excludedClasses.push(activityName);
}
}
}
}
+
+for(const generatedClass of generatedClasses){
+ console.log(generatedClass+"Test");
+}
+
+console.log("\nFinished generating " + generatedClasses.length + " activity sanity tests, excluded " + excludeClasses.length + " classes.\n"); \ No newline at end of file
diff --git a/platform/android/settings.gradle b/platform/android/settings.gradle
index 9be29f4bd4..b5ab80b5ec 100644
--- a/platform/android/settings.gradle
+++ b/platform/android/settings.gradle
@@ -1,3 +1 @@
-include ':MapboxGLAndroidSDK'
-include ':MapboxGLAndroidSDKTestApp'
-include ':MapboxGLAndroidSDKWearTestApp'
+include ':MapboxGLAndroidSDK', ':MapboxGLAndroidSDKTestApp' \ No newline at end of file
diff --git a/platform/android/src/android_renderer_backend.cpp b/platform/android/src/android_renderer_backend.cpp
new file mode 100755
index 0000000000..ae35acc5da
--- /dev/null
+++ b/platform/android/src/android_renderer_backend.cpp
@@ -0,0 +1,63 @@
+#include "android_renderer_backend.hpp"
+
+#include <mbgl/gl/context.hpp>
+
+#include <EGL/egl.h>
+
+#include <cassert>
+
+namespace mbgl {
+namespace android {
+
+/**
+ * From mbgl::View
+ */
+void AndroidRendererBackend::bind() {
+ assert(BackendScope::exists());
+ setFramebufferBinding(0);
+ setViewport(0, 0, getFramebufferSize());
+}
+
+/**
+ * From mbgl::RendererBackend.
+ */
+gl::ProcAddress AndroidRendererBackend::getExtensionFunctionPointer(const char* name) {
+ assert(BackendScope::exists());
+ return eglGetProcAddress(name);
+}
+
+void AndroidRendererBackend::updateViewPort() {
+ assert(BackendScope::exists());
+ setViewport(0, 0, getFramebufferSize());
+}
+
+void AndroidRendererBackend::resizeFramebuffer(int width, int height) {
+ fbWidth = width;
+ fbHeight = height;
+}
+
+PremultipliedImage AndroidRendererBackend::readFramebuffer() const {
+ assert(BackendScope::exists());
+ return RendererBackend::readFramebuffer(getFramebufferSize());
+}
+
+mbgl::Size AndroidRendererBackend::getFramebufferSize() const {
+ return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) };
+}
+
+/**
+ * From mbgl::RendererBackend.
+ */
+void AndroidRendererBackend::updateAssumedState() {
+ assumeFramebufferBinding(0);
+ assumeViewport(0, 0, getFramebufferSize());
+}
+
+void AndroidRendererBackend::markContextLost() {
+ if (context) {
+ context->setCleanupOnDestruction(false);
+ }
+}
+
+} // namespace android
+} // namspace mbgl
diff --git a/platform/android/src/android_renderer_backend.hpp b/platform/android/src/android_renderer_backend.hpp
new file mode 100755
index 0000000000..d2c100dcc1
--- /dev/null
+++ b/platform/android/src/android_renderer_backend.hpp
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <mbgl/renderer/renderer_backend.hpp>
+
+namespace mbgl {
+namespace android {
+
+class AndroidRendererBackend : public RendererBackend {
+public:
+
+ // mbgl::RendererBackend //
+ void bind() override;
+ void updateAssumedState() override;
+ mbgl::Size getFramebufferSize() const override;
+
+ // Ensures the current context is not
+ // cleaned up when destroyed
+ void markContextLost();
+
+ void updateViewPort();
+
+ void resizeFramebuffer(int width, int height);
+ PremultipliedImage readFramebuffer() const;
+
+protected:
+ // mbgl::RendererBackend //
+ gl::ProcAddress getExtensionFunctionPointer(const char*) override;
+ void activate() override {};
+ void deactivate() override {};
+
+
+private:
+
+ // Minimum texture size according to OpenGL ES 2.0 specification.
+ int fbWidth = 64;
+ int fbHeight = 64;
+
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp
new file mode 100644
index 0000000000..afdb08a10e
--- /dev/null
+++ b/platform/android/src/android_renderer_frontend.cpp
@@ -0,0 +1,122 @@
+#include "android_renderer_frontend.hpp"
+
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/util/thread.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include "android_renderer_backend.hpp"
+
+namespace mbgl {
+namespace android {
+
+// Forwards RendererObserver signals to the given
+// Delegate RendererObserver on the given RunLoop
+class ForwardingRendererObserver : public RendererObserver {
+public:
+ ForwardingRendererObserver(util::RunLoop& mapRunLoop, RendererObserver& delegate_)
+ : mailbox(std::make_shared<Mailbox>(mapRunLoop))
+ , delegate(delegate_, mailbox) {
+ }
+
+ ~ForwardingRendererObserver() {
+ mailbox->close();
+ }
+
+ void onInvalidate() override {
+ delegate.invoke(&RendererObserver::onInvalidate);
+ }
+
+ void onResourceError(std::exception_ptr err) override {
+ delegate.invoke(&RendererObserver::onResourceError, err);
+ }
+
+ void onWillStartRenderingMap() override {
+ delegate.invoke(&RendererObserver::onWillStartRenderingMap);
+ }
+
+ void onWillStartRenderingFrame() override {
+ delegate.invoke(&RendererObserver::onWillStartRenderingFrame);
+ }
+
+ void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) override {
+ delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded);
+ }
+
+ void onDidFinishRenderingMap() override {
+ delegate.invoke(&RendererObserver::onDidFinishRenderingMap);
+ }
+
+private:
+ std::shared_ptr<Mailbox> mailbox;
+ ActorRef<RendererObserver> delegate;
+};
+
+AndroidRendererFrontend::AndroidRendererFrontend(MapRenderer& mapRenderer_)
+ : mapRenderer(mapRenderer_)
+ , mapRunLoop(util::RunLoop::Get()) {
+}
+
+AndroidRendererFrontend::~AndroidRendererFrontend() = default;
+
+void AndroidRendererFrontend::reset() {
+ mapRenderer.reset();
+}
+
+void AndroidRendererFrontend::setObserver(RendererObserver& observer) {
+ assert (util::RunLoop::Get());
+ // Don't call the Renderer directly, but use MapRenderer#setObserver to make sure
+ // the Renderer may be re-initialised without losing the RendererObserver reference.
+ mapRenderer.setObserver(std::make_unique<ForwardingRendererObserver>(*mapRunLoop, observer));
+}
+
+void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) {
+ mapRenderer.update(std::move(params));
+ mapRenderer.requestRender();
+}
+
+void AndroidRendererFrontend::onLowMemory() {
+ mapRenderer.actor().invoke(&Renderer::onLowMemory);
+}
+
+std::vector<Feature> AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID,
+ const SourceQueryOptions& options) const {
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(&Renderer::querySourceFeatures, sourceID, options).get();
+}
+
+std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenBox& box,
+ const RenderedQueryOptions& options) const {
+
+ // Select the right overloaded method
+ std::vector<Feature> (Renderer::*fn)(const ScreenBox&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures;
+
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(fn, box, options).get();
+}
+
+std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenCoordinate& point,
+ const RenderedQueryOptions& options) const {
+
+ // Select the right overloaded method
+ std::vector<Feature> (Renderer::*fn)(const ScreenCoordinate&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures;
+
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(fn, point, options).get();
+}
+
+AnnotationIDs AndroidRendererFrontend::queryPointAnnotations(const ScreenBox& box) const {
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(&Renderer::queryPointAnnotations, box).get();
+}
+
+AnnotationIDs AndroidRendererFrontend::queryShapeAnnotations(const ScreenBox& box) const {
+ // Waits for the result from the orchestration thread and returns
+ return mapRenderer.actor().ask(&Renderer::queryShapeAnnotations, box).get();
+}
+
+} // namespace android
+} // namespace mbgl
+
diff --git a/platform/android/src/android_renderer_frontend.hpp b/platform/android/src/android_renderer_frontend.hpp
new file mode 100644
index 0000000000..178870c452
--- /dev/null
+++ b/platform/android/src/android_renderer_frontend.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/actor/actor.hpp>
+#include <mbgl/annotation/annotation.hpp>
+#include <mbgl/renderer/renderer_frontend.hpp>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <functional>
+#include <memory>
+#include <vector>
+#include <string>
+
+#include "map_renderer.hpp"
+
+namespace mbgl {
+
+class RenderedQueryOptions;
+class SourceQueryOptions;
+
+namespace android {
+
+class AndroidRendererFrontend : public RendererFrontend {
+public:
+
+ AndroidRendererFrontend(MapRenderer&);
+ ~AndroidRendererFrontend() override;
+
+ void reset() override;
+ void setObserver(RendererObserver&) override;
+
+ void update(std::shared_ptr<UpdateParameters>) override;
+
+ // Feature querying
+ std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions&) const;
+ std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions&) const;
+ std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const;
+ AnnotationIDs queryPointAnnotations(const ScreenBox& box) const;
+ AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const;
+
+ // Memory
+ void onLowMemory();
+
+private:
+ MapRenderer& mapRenderer;
+ util::RunLoop* mapRunLoop;
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp
index c55c9c3527..1ed68d0835 100644
--- a/platform/android/src/example_custom_layer.cpp
+++ b/platform/android/src/example_custom_layer.cpp
@@ -92,6 +92,10 @@ void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters&
reinterpret_cast<ExampleCustomLayer*>(context)->render();
}
+void nativeContextLost(void */*context*/) {
+ mbgl::Log::Info(mbgl::Event::General, "nativeContextLost");
+}
+
void nativeDenitialize(void *context) {
mbgl::Log::Info(mbgl::Event::General, "nativeDeinitialize");
delete reinterpret_cast<ExampleCustomLayer*>(context);
@@ -123,6 +127,10 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
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>(nativeDenitialize));
diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp
index 5d19c506bc..6a9d7badb0 100644
--- a/platform/android/src/file_source.cpp
+++ b/platform/android/src/file_source.cpp
@@ -1,15 +1,13 @@
#include "file_source.hpp"
#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/util/logging.hpp>
-#include <mbgl/util/run_loop.hpp>
#include "asset_manager_file_source.hpp"
#include "jni/generic_global_ref_deleter.hpp"
-#include <string>
-
namespace mbgl {
namespace android {
@@ -45,7 +43,7 @@ void FileSource::setAPIBaseUrl(jni::JNIEnv& env, jni::String url) {
void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> transformCallback) {
if (transformCallback) {
- resourceTransform = std::make_unique<Actor<ResourceTransform>>(*util::RunLoop::Get(),
+ resourceTransform = std::make_unique<Actor<ResourceTransform>>(*Scheduler::GetCurrent(),
// Capture the ResourceTransformCallback object as a managed global into
// the lambda. It is released automatically when we're setting a new ResourceTransform in
// a subsequent call.
@@ -64,6 +62,25 @@ void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::
}
}
+void FileSource::resume(jni::JNIEnv&) {
+ if (!activationCounter) {
+ activationCounter = optional<int>(1) ;
+ return;
+ }
+
+ activationCounter.value()++;
+ if (activationCounter == 1) {
+ fileSource->resume();
+ }
+}
+
+void FileSource::pause(jni::JNIEnv&) {
+ activationCounter.value()--;
+ if (activationCounter == 0) {
+ fileSource->pause();
+ }
+}
+
jni::Class<FileSource> FileSource::javaClass;
FileSource* FileSource::getNativePeer(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) {
@@ -93,7 +110,9 @@ void FileSource::registerNative(jni::JNIEnv& env) {
METHOD(&FileSource::getAccessToken, "getAccessToken"),
METHOD(&FileSource::setAccessToken, "setAccessToken"),
METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"),
- METHOD(&FileSource::setResourceTransform, "setResourceTransform")
+ METHOD(&FileSource::setResourceTransform, "setResourceTransform"),
+ METHOD(&FileSource::resume, "activate"),
+ METHOD(&FileSource::pause, "deactivate")
);
}
diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp
index 4abe352bff..194f784622 100644
--- a/platform/android/src/file_source.hpp
+++ b/platform/android/src/file_source.hpp
@@ -41,6 +41,10 @@ public:
void setResourceTransform(jni::JNIEnv&, jni::Object<FileSource::ResourceTransformCallback>);
+ void resume(jni::JNIEnv&);
+
+ void pause(jni::JNIEnv&);
+
static jni::Class<FileSource> javaClass;
static FileSource* getNativePeer(jni::JNIEnv&, jni::Object<FileSource>);
@@ -50,6 +54,7 @@ public:
static void registerNative(jni::JNIEnv&);
private:
+ optional<int> activationCounter;
std::unique_ptr<Actor<ResourceTransform>> resourceTransform;
std::unique_ptr<mbgl::DefaultFileSource> fileSource;
};
diff --git a/platform/android/src/geometry/lat_lng_bounds.cpp b/platform/android/src/geometry/lat_lng_bounds.cpp
index 9efacde120..ec1a32fed5 100644
--- a/platform/android/src/geometry/lat_lng_bounds.cpp
+++ b/platform/android/src/geometry/lat_lng_bounds.cpp
@@ -9,10 +9,10 @@ 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, "mLatSouth");
- static auto swLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLonWest");
- static auto neLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLatNorth");
- static auto neLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLonEast");
+ 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) }
diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp
index ee1429bd74..8eb9416e9d 100644
--- a/platform/android/src/http_file_source.cpp
+++ b/platform/android/src/http_file_source.cpp
@@ -117,7 +117,9 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code,
}
if (cacheControl) {
- response.expires = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()).toTimePoint();
+ const auto cc = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str());
+ response.expires = cc.toTimePoint();
+ response.mustRevalidate = cc.mustRevalidate;
}
if (expires) {
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index db8dd1dbdf..88ad0edb9e 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -32,6 +32,8 @@
#include "gson/json_object.hpp"
#include "gson/json_primitive.hpp"
#include "java_types.hpp"
+#include "map_renderer.hpp"
+#include "map_renderer_runnable.hpp"
#include "native_map_view.hpp"
#include "offline/offline_manager.hpp"
#include "offline/offline_region.hpp"
@@ -45,8 +47,11 @@
#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"
namespace mbgl {
namespace android {
@@ -143,6 +148,8 @@ void registerNatives(JavaVM *vm) {
Polyline::registerNative(env);
// Map
+ MapRenderer::registerNative(env);
+ MapRendererRunnable::registerNative(env);
NativeMapView::registerNative(env);
// Http
@@ -155,7 +162,7 @@ void registerNatives(JavaVM *vm) {
// Style
TransitionOptions::registerNative(env);
registerNativeLayers(env);
- registerNativeSources(env);
+ Source::registerNative(env);
Light::registerNative(env);
Position::registerNative(env);
Stop::registerNative(env);
@@ -166,6 +173,7 @@ void registerNatives(JavaVM *vm) {
// Map
CameraPosition::registerNative(env);
+ Image::registerNative(env);
// Connectivity
ConnectivityListener::registerNative(env);
@@ -177,6 +185,13 @@ void registerNatives(JavaVM *vm) {
OfflineTilePyramidRegionDefinition::registerNative(env);
OfflineRegionError::registerNative(env);
OfflineRegionStatus::registerNative(env);
+
+ // Snapshotter
+ MapSnapshotter::registerNative(env);
+ MapSnapshot::registerNative(env);
+
+ // text
+ LocalGlyphRasterizer::registerNative(env);
}
} // namespace android
diff --git a/platform/android/src/jni/collection.hpp b/platform/android/src/jni/collection.hpp
new file mode 100644
index 0000000000..5f94ec29ce
--- /dev/null
+++ b/platform/android/src/jni/collection.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+#include <string>
+#include <vector>
+
+namespace jni {
+
+inline Array<String> MakeAnything(ThingToMake<Array<String>>, JNIEnv& env, const std::vector<std::string>& vector)
+{
+ static auto clazz = *Class<StringTag>::Find(env).NewGlobalRef(env).release();
+ auto result = Array<String>::New(env, vector.size(), clazz);
+
+ std::size_t index = 0;
+ for (auto&& item : vector) {
+ auto element = Make<jni::String>(env, item);
+ result.Set(env, index, element);
+ DeleteLocalRef(env, element);
+ index++;
+ }
+
+ return result;
+}
+
+}
diff --git a/platform/android/src/jni/generic_global_ref_deleter.hpp b/platform/android/src/jni/generic_global_ref_deleter.hpp
index 4e53e0a0ce..7239e361a7 100644
--- a/platform/android/src/jni/generic_global_ref_deleter.hpp
+++ b/platform/android/src/jni/generic_global_ref_deleter.hpp
@@ -18,5 +18,34 @@ struct GenericGlobalRefDeleter {
}
};
+
+template < class TagType >
+class GenericWeakObjectRefDeleter;
+
+template < class TagType = jni::ObjectTag >
+using GenericUniqueWeakObject = std::unique_ptr< const jni::Object<TagType>, GenericWeakObjectRefDeleter<TagType> >;
+
+template < class TagType >
+class GenericWeakObjectRefDeleter
+{
+public:
+ using pointer = jni::PointerToValue< jni::Object<TagType> >;
+
+ void operator()(pointer p) const
+ {
+ if (p)
+ {
+ auto env = AttachEnv();
+ env->DeleteWeakGlobalRef(jni::Unwrap(p->Get()));
+ }
+ }
+};
+
+template < class TagType >
+GenericUniqueWeakObject<TagType> SeizeGenericWeakRef(JNIEnv&, jni::Object<TagType>&& object)
+{
+ return GenericUniqueWeakObject<TagType>(jni::PointerToValue<jni::Object<TagType>>(std::move(object)), GenericWeakObjectRefDeleter<TagType>());
+};
+
} // namespace android
} // namespace mbgl
diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp
index d6f2cb83e8..1fc5f9789f 100644
--- a/platform/android/src/map/camera_position.cpp
+++ b/platform/android/src/map/camera_position.cpp
@@ -27,6 +27,24 @@ jni::Object<CameraPosition> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOp
return CameraPosition::javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees);
}
+mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, jni::Object<CameraPosition> position) {
+ static auto bearing = CameraPosition::javaClass.GetField<jni::jdouble>(env, "bearing");
+ static auto target = CameraPosition::javaClass.GetField<jni::Object<LatLng>>(env, "target");
+ static auto tilt = CameraPosition::javaClass.GetField<jni::jdouble>(env, "tilt");
+ static auto zoom = CameraPosition::javaClass.GetField<jni::jdouble>(env, "zoom");
+
+ auto center = LatLng::getLatLng(env, position.Get(env, target));
+
+ return mbgl::CameraOptions {
+ center,
+ {},
+ {},
+ position.Get(env, zoom),
+ position.Get(env, bearing) * util::DEG2RAD,
+ position.Get(env, tilt)
+ };
+}
+
void CameraPosition::registerNative(jni::JNIEnv &env) {
// Lookup the class
CameraPosition::javaClass = *jni::Class<CameraPosition>::Find(env).NewGlobalRef(env).release();
diff --git a/platform/android/src/map/camera_position.hpp b/platform/android/src/map/camera_position.hpp
index b9f1646cc9..4eee8be758 100644
--- a/platform/android/src/map/camera_position.hpp
+++ b/platform/android/src/map/camera_position.hpp
@@ -15,6 +15,8 @@ public:
static jni::Object<CameraPosition> New(jni::JNIEnv&, mbgl::CameraOptions);
+ static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, jni::Object<CameraPosition>);
+
static jni::Class<CameraPosition> javaClass;
static void registerNative(jni::JNIEnv&);
diff --git a/platform/android/src/map/image.cpp b/platform/android/src/map/image.cpp
new file mode 100644
index 0000000000..5f5c90eddd
--- /dev/null
+++ b/platform/android/src/map/image.cpp
@@ -0,0 +1,44 @@
+#include <mbgl/style/image.hpp>
+#include <mbgl/util/exception.hpp>
+#include "image.hpp"
+
+namespace mbgl {
+namespace android {
+
+mbgl::style::Image Image::getImage(jni::JNIEnv& env, jni::Object<Image> image) {
+ static auto widthField = Image::javaClass.GetField<jni::jint>(env, "width");
+ static auto heightField = Image::javaClass.GetField<jni::jint>(env, "height");
+ static auto pixelRatioField = Image::javaClass.GetField<jni::jfloat>(env, "pixelRatio");
+ static auto bufferField = Image::javaClass.GetField<jni::Array<jbyte>>(env, "buffer");
+ static auto nameField = Image::javaClass.GetField<jni::String>(env, "name");
+
+ auto height = image.Get(env, heightField);
+ auto width = image.Get(env, widthField);
+ auto pixelRatio = image.Get(env, pixelRatioField);
+ auto pixels = image.Get(env, bufferField);
+ auto name = jni::Make<std::string>(env, image.Get(env, nameField));
+
+ jni::NullCheck(env, &pixels);
+ std::size_t size = pixels.Length(env);
+
+ mbgl::PremultipliedImage premultipliedImage({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
+ 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()));
+
+ return mbgl::style::Image {name, std::move(premultipliedImage), pixelRatio};
+}
+
+void Image::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ Image::javaClass = *jni::Class<Image>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Image> Image::javaClass;
+
+
+} // namespace android
+} // namespace mb
+
diff --git a/platform/android/src/map/image.hpp b/platform/android/src/map/image.hpp
new file mode 100644
index 0000000000..1513e13ee7
--- /dev/null
+++ b/platform/android/src/map/image.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+#include <mbgl/style/image.hpp>
+
+namespace mbgl {
+namespace android {
+
+class Image : private mbgl::util::noncopyable {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/Image"; };
+
+ static mbgl::style::Image getImage(jni::JNIEnv&, jni::Object<Image>);
+
+ static jni::Class<Image> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+};
+
+
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp
new file mode 100644
index 0000000000..2440ac93ef
--- /dev/null
+++ b/platform/android/src/map_renderer.cpp
@@ -0,0 +1,206 @@
+#include "map_renderer.hpp"
+
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <string>
+
+#include "attach_env.hpp"
+#include "android_renderer_backend.hpp"
+#include "map_renderer_runnable.hpp"
+#include "file_source.hpp"
+
+namespace mbgl {
+namespace android {
+
+MapRenderer::MapRenderer(jni::JNIEnv& _env, jni::Object<MapRenderer> obj,
+ jni::Object<FileSource> _fileSource, jni::jfloat pixelRatio_,
+ jni::String programCacheDir_,
+ jni::String localIdeographFontFamily_)
+ : javaPeer(SeizeGenericWeakRef(_env, jni::Object<MapRenderer>(jni::NewWeakGlobalRef(_env, obj.Get()).release()))), pixelRatio(pixelRatio_)
+ , fileSource(FileSource::getDefaultFileSource(_env, _fileSource))
+ , programCacheDir(jni::Make<std::string>(_env, programCacheDir_))
+ , localIdeographFontFamily(localIdeographFontFamily_ == nullptr ? optional<std::string>{} : jni::Make<std::string>(_env, localIdeographFontFamily_ ))
+ , threadPool(sharedThreadPool())
+ , mailbox(std::make_shared<Mailbox>(*this)) {
+}
+
+MapRenderer::~MapRenderer() = default;
+
+void MapRenderer::reset() {
+ // Make sure to destroy the renderer on the GL Thread
+ auto self = ActorRef<MapRenderer>(*this, mailbox);
+ self.ask(&MapRenderer::resetRenderer).wait();
+
+ // Lock to make sure there is no concurrent initialisation on the gl thread
+ std::lock_guard<std::mutex> lock(initialisationMutex);
+ rendererObserver.reset();
+}
+
+ActorRef<Renderer> MapRenderer::actor() const {
+ return *rendererRef;
+}
+
+void MapRenderer::schedule(std::weak_ptr<Mailbox> scheduled) {
+ // Create a runnable
+ android::UniqueEnv _env = android::AttachEnv();
+ auto runnable = std::make_unique<MapRendererRunnable>(*_env, std::move(scheduled));
+
+ // Obtain ownership of the peer (gets transferred to the MapRenderer on the JVM for later GC)
+ auto peer = runnable->peer();
+
+ // Queue the event on the Java Peer
+ static auto queueEvent = javaClass.GetMethod<void(
+ jni::Object<MapRendererRunnable>)>(*_env, "queueEvent");
+ javaPeer->Call(*_env, queueEvent, *peer);
+
+ // Release the c++ peer as it will be destroyed on GC of the Java Peer
+ runnable.release();
+}
+
+void MapRenderer::requestRender() {
+ android::UniqueEnv _env = android::AttachEnv();
+ static auto onInvalidate = javaClass.GetMethod<void()>(*_env, "requestRender");
+ javaPeer->Call(*_env, onInvalidate);
+}
+
+void MapRenderer::update(std::shared_ptr<UpdateParameters> params) {
+ // Lock on the parameters
+ std::lock_guard<std::mutex> lock(updateMutex);
+ updateParameters = std::move(params);
+}
+
+void MapRenderer::setObserver(std::shared_ptr<RendererObserver> _rendererObserver) {
+ // Lock as the initialization can come from the main thread or the GL thread first
+ std::lock_guard<std::mutex> lock(initialisationMutex);
+
+ rendererObserver = std::move(_rendererObserver);
+
+ // Set the new observer on the Renderer implementation
+ if (renderer) {
+ renderer->setObserver(rendererObserver.get());
+ }
+}
+
+void MapRenderer::requestSnapshot(SnapshotCallback callback) {
+ auto self = ActorRef<MapRenderer>(*this, mailbox);
+ self.invoke(
+ &MapRenderer::scheduleSnapshot,
+ std::make_unique<SnapshotCallback>([&, callback=std::move(callback), runloop=util::RunLoop::Get()](PremultipliedImage image) {
+ runloop->invoke([callback=std::move(callback), image=std::move(image)]() mutable {
+ callback(std::move(image));
+ });
+ snapshotCallback.reset();
+ })
+ );
+}
+
+// Called on OpenGL thread //
+
+void MapRenderer::resetRenderer() {
+ assert (renderer);
+ renderer.reset();
+}
+
+void MapRenderer::scheduleSnapshot(std::unique_ptr<SnapshotCallback> callback) {
+ snapshotCallback = std::move(callback);
+ requestRender();
+}
+
+void MapRenderer::render(JNIEnv&) {
+ assert (renderer);
+
+ std::shared_ptr<UpdateParameters> params;
+ {
+ // Lock on the parameters
+ std::unique_lock<std::mutex> lock(updateMutex);
+ if (!updateParameters) return;
+
+ // Hold on to the update parameters during render
+ params = updateParameters;
+ }
+
+ // Activate the backend
+ BackendScope backendGuard { *backend };
+
+ // Ensure that the "current" scheduler on the render thread is
+ // this scheduler.
+ Scheduler::SetCurrent(this);
+
+ if (framebufferSizeChanged) {
+ backend->updateViewPort();
+ framebufferSizeChanged = false;
+ }
+
+ renderer->render(*params);
+
+ // Deliver the snapshot if requested
+ if (snapshotCallback) {
+ snapshotCallback->operator()(backend->readFramebuffer());
+ snapshotCallback.reset();
+ }
+}
+
+void MapRenderer::onSurfaceCreated(JNIEnv&) {
+ // Lock as the initialization can come from the main thread or the GL thread first
+ std::lock_guard<std::mutex> lock(initialisationMutex);
+
+ // The android system will have already destroyed the underlying
+ // GL resources if this is not the first initialization and an
+ // attempt to clean them up will fail
+ if (backend) backend->markContextLost();
+ if (renderer) renderer->markContextLost();
+
+ // Reset in opposite order
+ renderer.reset();
+ backend.reset();
+
+ // Create the new backend and renderer
+ backend = std::make_unique<AndroidRendererBackend>();
+ renderer = std::make_unique<Renderer>(*backend, pixelRatio, fileSource, *threadPool,
+ GLContextMode::Unique, programCacheDir, localIdeographFontFamily);
+ rendererRef = std::make_unique<ActorRef<Renderer>>(*renderer, mailbox);
+
+ // Set the observer on the new Renderer implementation
+ if (rendererObserver) {
+ renderer->setObserver(rendererObserver.get());
+ }
+}
+
+void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) {
+ backend->resizeFramebuffer(width, height);
+ framebufferSizeChanged = true;
+ requestRender();
+}
+
+// Static methods //
+
+jni::Class<MapRenderer> MapRenderer::javaClass;
+
+void MapRenderer::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ MapRenderer::javaClass = *jni::Class<MapRenderer>::Find(env).NewGlobalRef(env).release();
+
+#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<MapRenderer>(env, MapRenderer::javaClass, "nativePtr",
+ std::make_unique<MapRenderer, JNIEnv&, jni::Object<MapRenderer>, jni::Object<FileSource>, jni::jfloat, jni::String, jni::String>,
+ "nativeInitialize", "finalize",
+ METHOD(&MapRenderer::render, "nativeRender"),
+ METHOD(&MapRenderer::onSurfaceCreated,
+ "nativeOnSurfaceCreated"),
+ METHOD(&MapRenderer::onSurfaceChanged,
+ "nativeOnSurfaceChanged"));
+}
+
+MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object<MapRenderer> jObject) {
+ static auto field = MapRenderer::javaClass.GetField<jlong>(env, "nativePtr");
+ MapRenderer* mapRenderer = reinterpret_cast<MapRenderer*>(jObject.Get(env, field));
+ assert(mapRenderer != nullptr);
+ return *mapRenderer;
+}
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp
new file mode 100644
index 0000000000..c36357af7a
--- /dev/null
+++ b/platform/android/src/map_renderer.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/util/image.hpp>
+
+#include <memory>
+#include <utility>
+
+#include <jni/jni.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+
+#include "jni/generic_global_ref_deleter.hpp"
+
+namespace mbgl {
+
+template <class>
+class ActorRef;
+class Mailbox;
+class Renderer;
+class RendererBackend;
+class RendererObserver;
+class ThreadPool;
+class UpdateParameters;
+
+namespace android {
+
+class AndroidRendererBackend;
+class FileSource;
+
+/**
+ * The MapRenderer is a peer class that encapsulates the actions
+ * performed on the GL Thread.
+ *
+ * The public methods are safe to call from the main thread, others are not.
+ */
+class MapRenderer : public Scheduler {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/renderer/MapRenderer"; };
+
+ static jni::Class<MapRenderer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ static MapRenderer& getNativePeer(JNIEnv&, jni::Object<MapRenderer>);
+
+ MapRenderer(jni::JNIEnv& _env,
+ jni::Object<MapRenderer>,
+ jni::Object<FileSource>,
+ jni::jfloat pixelRatio,
+ jni::String programCacheDir,
+ jni::String localIdeographFontFamily);
+
+ ~MapRenderer() override;
+
+ // Resets the renderer to clean up on the calling thread
+ void reset();
+
+ // Takes the RendererObserver by shared_ptr so we
+ // don't have to make the header public. Use
+ // this instead of Renderer#setObserver directly
+ void setObserver(std::shared_ptr<RendererObserver>);
+
+ // Sets the new update parameters to use on subsequent
+ // renders. Be sure to trigger a render with
+ // requestRender().
+ void update(std::shared_ptr<UpdateParameters>);
+
+ // Gives a handle to the Renderer to enable actions on
+ // any thread.
+ ActorRef<Renderer> actor() const;
+
+ // From Scheduler. Schedules by using callbacks to the
+ // JVM to process the mailbox on the right thread.
+ void schedule(std::weak_ptr<Mailbox> scheduled) override;
+
+ void requestRender();
+
+ // Snapshot - requires a RunLoop on the calling thread
+ using SnapshotCallback = std::function<void (PremultipliedImage)>;
+ void requestSnapshot(SnapshotCallback);
+
+protected:
+ // Called from the GL Thread //
+
+ void scheduleSnapshot(std::unique_ptr<SnapshotCallback>);
+
+private:
+ // Called from the GL Thread //
+
+ // Resets the renderer
+ void resetRenderer();
+
+ // Renders a frame.
+ void render(JNIEnv&);
+
+ void onSurfaceCreated(JNIEnv&);
+
+ void onSurfaceChanged(JNIEnv&, jint width, jint height);
+
+private:
+ GenericUniqueWeakObject<MapRenderer> javaPeer;
+
+ float pixelRatio;
+ DefaultFileSource& fileSource;
+ std::string programCacheDir;
+ optional<std::string> localIdeographFontFamily;
+
+ std::shared_ptr<ThreadPool> threadPool;
+ std::shared_ptr<Mailbox> mailbox;
+
+ std::mutex initialisationMutex;
+ std::shared_ptr<RendererObserver> rendererObserver;
+
+ std::unique_ptr<AndroidRendererBackend> backend;
+ std::unique_ptr<Renderer> renderer;
+ std::unique_ptr<ActorRef<Renderer>> rendererRef;
+
+ std::shared_ptr<UpdateParameters> updateParameters;
+ std::mutex updateMutex;
+
+ bool framebufferSizeChanged = false;
+
+ std::unique_ptr<SnapshotCallback> snapshotCallback;
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/map_renderer_runnable.cpp b/platform/android/src/map_renderer_runnable.cpp
new file mode 100644
index 0000000000..4dc6611c40
--- /dev/null
+++ b/platform/android/src/map_renderer_runnable.cpp
@@ -0,0 +1,51 @@
+#include "map_renderer_runnable.hpp"
+
+#include <mbgl/util/logging.hpp>
+
+namespace mbgl {
+namespace android {
+
+MapRendererRunnable::MapRendererRunnable(jni::JNIEnv& env, std::weak_ptr<Mailbox> mailbox_)
+ : mailbox(std::move(mailbox_)) {
+
+ // Create the Java peer and hold on to a global reference
+ // Not using a weak reference here as this might oerflow
+ // the weak reference table on some devices
+ jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 5);
+ static auto constructor = javaClass.GetConstructor<jlong>(env);
+ auto instance = javaClass.New(env, constructor, reinterpret_cast<jlong>(this));
+ javaPeer = instance.NewGlobalRef(env);
+}
+
+MapRendererRunnable::~MapRendererRunnable() = default;
+
+void MapRendererRunnable::run(jni::JNIEnv&) {
+ Mailbox::maybeReceive(mailbox);
+}
+
+jni::UniqueObject<MapRendererRunnable> MapRendererRunnable::peer() {
+ return std::move(javaPeer);
+}
+
+// Static methods //
+
+jni::Class<MapRendererRunnable> MapRendererRunnable::javaClass;
+
+void MapRendererRunnable::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ MapRendererRunnable::javaClass = *jni::Class<MapRendererRunnable>::Find(env).NewGlobalRef(env).release();
+
+#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ jni::RegisterNativePeer<MapRendererRunnable>(
+ env,
+ MapRendererRunnable::javaClass,
+ "nativePtr",
+ std::make_unique<MapRendererRunnable, JNIEnv&>,
+ "nativeInitialize",
+ "finalize",
+ METHOD(&MapRendererRunnable::run, "run"));
+}
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/map_renderer_runnable.hpp b/platform/android/src/map_renderer_runnable.hpp
new file mode 100644
index 0000000000..46fb028d26
--- /dev/null
+++ b/platform/android/src/map_renderer_runnable.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mbgl/actor/mailbox.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+#include <memory>
+#include <utility>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+/**
+ * The MapRendererRunnable is a peer class that encapsulates
+ * a scheduled mailbox in a Java Runnable so it can be
+ * scheduled on the map renderer thread.
+ *
+ */
+class MapRendererRunnable {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable"; };
+
+ static jni::Class<MapRendererRunnable> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ MapRendererRunnable(jni::JNIEnv&, std::weak_ptr<Mailbox>);
+
+ // Only for jni registration, unused
+ MapRendererRunnable(jni::JNIEnv&) {
+ assert(false);
+ }
+
+ ~MapRendererRunnable();
+
+ void run(jni::JNIEnv&);
+
+ // Transfers ownership of the Peer object to the caller
+ jni::UniqueObject<MapRendererRunnable> peer();
+
+private:
+ jni::UniqueObject<MapRendererRunnable> javaPeer;
+ std::weak_ptr<Mailbox> mailbox;
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 13d5be01b9..36a73fee35 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -9,12 +9,10 @@
#include <sys/system_properties.h>
-#include <EGL/egl.h>
#include <android/native_window_jni.h>
#include <jni/jni.hpp>
-#include <mbgl/map/backend_scope.hpp>
#include <mbgl/math/minmax.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/event.hpp>
@@ -28,6 +26,7 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/filter.hpp>
+#include <mbgl/renderer/query.hpp>
// Java -> C++ conversion
#include "style/android_conversion.hpp"
@@ -42,12 +41,17 @@
#include "jni.hpp"
#include "attach_env.hpp"
+#include "map_renderer.hpp"
+#include "android_renderer_frontend.hpp"
+#include "file_source.hpp"
#include "bitmap.hpp"
#include "run_loop_impl.hpp"
#include "java/util.hpp"
#include "geometry/lat_lng_bounds.hpp"
#include "map/camera_position.hpp"
+#include "map/image.hpp"
#include "style/light.hpp"
+#include "bitmap_factory.hpp"
namespace mbgl {
namespace android {
@@ -55,15 +59,12 @@ namespace android {
NativeMapView::NativeMapView(jni::JNIEnv& _env,
jni::Object<NativeMapView> _obj,
jni::Object<FileSource> jFileSource,
- jni::jfloat _pixelRatio,
- jni::String _programCacheDir,
- jni::jint _availableProcessors,
- jni::jlong _totalMemory)
- : javaPeer(_obj.NewWeakGlobalRef(_env)),
- pixelRatio(_pixelRatio),
- availableProcessors(_availableProcessors),
- totalMemory(_totalMemory),
- threadPool(sharedThreadPool()) {
+ jni::Object<MapRenderer> jMapRenderer,
+ jni::jfloat _pixelRatio)
+ : javaPeer(_obj.NewWeakGlobalRef(_env))
+ , mapRenderer(MapRenderer::getNativePeer(_env, jMapRenderer))
+ , pixelRatio(_pixelRatio)
+ , threadPool(sharedThreadPool()) {
// Get a reference to the JavaVM for callbacks
if (_env.GetJavaVM(&vm) < 0) {
@@ -71,123 +72,30 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env,
return;
}
- // Create the core map
- map = std::make_unique<mbgl::Map>(
- *this, mbgl::Size{ static_cast<uint32_t>(width), static_cast<uint32_t>(height) },
- pixelRatio, mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource), *threadPool,
- MapMode::Continuous, GLContextMode::Unique, ConstrainMode::HeightOnly,
- ViewportMode::Default, jni::Make<std::string>(_env, _programCacheDir));
-
- recalculateSourceTileCacheSize();
-}
+ // Get native peer for file source
+ mbgl::FileSource& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource);
-void NativeMapView::recalculateSourceTileCacheSize() {
- //Calculate a fitting cache size based on device parameters
- float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1;
- float cpuFactor = availableProcessors;
- float memoryFactor = static_cast<float>(totalMemory) / 1000.0f / 1000.0f / 1000.0f;
- float sizeFactor = (static_cast<float>(map->getSize().width) / mbgl::util::tileSize) *
- (static_cast<float>(map->getSize().height) / mbgl::util::tileSize);
+ // Create a renderer frontend
+ rendererFrontend = std::make_unique<AndroidRendererFrontend>(mapRenderer);
- map->setSourceTileCacheSize(zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f);
+ // Create the core map
+ map = std::make_unique<mbgl::Map>(*rendererFrontend, *this,
+ mbgl::Size{ static_cast<uint32_t>(width),
+ static_cast<uint32_t>(height) }, pixelRatio,
+ fileSource, *threadPool, MapMode::Continuous,
+ ConstrainMode::HeightOnly, ViewportMode::Default);
}
/**
* Called through NativeMapView#destroy()
*/
NativeMapView::~NativeMapView() {
- _terminateContext();
- _destroySurface();
- _terminateDisplay();
-
map.reset();
-
vm = nullptr;
}
/**
- * From mbgl::View
- */
-void NativeMapView::bind() {
- setFramebufferBinding(0);
- setViewport(0, 0, getFramebufferSize());
-}
-
-/**
- * From mbgl::Backend.
- */
-gl::ProcAddress NativeMapView::initializeExtension(const char* name) {
- return eglGetProcAddress(name);
-}
-
-void NativeMapView::activate() {
- if (active++) {
- return;
- }
-
- oldDisplay = eglGetCurrentDisplay();
- oldReadSurface = eglGetCurrentSurface(EGL_READ);
- oldDrawSurface = eglGetCurrentSurface(EGL_DRAW);
- oldContext = eglGetCurrentContext();
-
- assert(vm != nullptr);
-
- if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) {
- if (!eglMakeCurrent(display, surface, surface, context)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
- eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
-
- if (!eglSwapInterval(display, 0)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError());
- throw std::runtime_error("eglSwapInterval() failed");
- }
- } else {
- mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready");
- }
-}
-
-/**
- * From mbgl::Backend.
- */
-void NativeMapView::deactivate() {
- if (--active) {
- return;
- }
-
- assert(vm != nullptr);
-
- if (oldContext != context && oldContext != EGL_NO_CONTEXT) {
- if (!eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
- eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
- } else if (display != EGL_NO_DISPLAY) {
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d",
- eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
- } else {
- mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready");
- }
-}
-
-/**
- * From mbgl::Backend. Callback to java NativeMapView#onInvalidate().
- *
- * May be called from any thread
- */
-void NativeMapView::invalidate() {
- android::UniqueEnv _env = android::AttachEnv();
- static auto onInvalidate = javaClass.GetMethod<void ()>(*_env, "onInvalidate");
- javaPeer->Call(*_env, onInvalidate);
-}
-
-/**
- * From mbgl::Backend. Callback to java NativeMapView#onMapChanged(int).
+ * From mbgl::RendererBackend. Callback to java NativeMapView#onMapChanged(int).
*
* May be called from any thread
*/
@@ -265,82 +173,10 @@ void NativeMapView::onSourceChanged(mbgl::style::Source&) {
// JNI Methods //
-void NativeMapView::initializeDisplay(jni::JNIEnv&) {
- _initializeDisplay();
-}
-
-void NativeMapView::terminateDisplay(jni::JNIEnv&) {
- _terminateDisplay();
-}
-
-void NativeMapView::initializeContext(jni::JNIEnv&) {
- _initializeContext();
-}
-
-void NativeMapView::terminateContext(jni::JNIEnv&) {
- _terminateContext();
-}
-
-void NativeMapView::createSurface(jni::JNIEnv& env, jni::Object<> _surface) {
- _createSurface(ANativeWindow_fromSurface(&env, jni::Unwrap(*_surface)));
-}
-
-void NativeMapView::destroySurface(jni::JNIEnv&) {
- _destroySurface();
-}
-
-void NativeMapView::render(jni::JNIEnv& env) {
- BackendScope guard(*this);
-
- if (framebufferSizeChanged) {
- setViewport(0, 0, getFramebufferSize());
- framebufferSizeChanged = false;
- }
-
- map->render(*this);
-
- if(snapshot){
- snapshot = false;
-
- // take snapshot
- auto image = readFramebuffer(getFramebufferSize());
- auto bitmap = Bitmap::CreateBitmap(env, std::move(image));
-
- // invoke Mapview#OnSnapshotReady
- android::UniqueEnv _env = android::AttachEnv();
- static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
- javaPeer->Call(*_env, onSnapshotReady, bitmap);
- }
-
- if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
- if (!eglSwapBuffers(display, surface)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
- eglGetError());
- throw std::runtime_error("eglSwapBuffers() failed");
- }
-
- updateFps();
- } else {
- mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
- }
-}
-
-void NativeMapView::update(jni::JNIEnv&) {
- invalidate();
-}
-
void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) {
width = util::max(64, w);
height = util::max(64, h);
map->setSize({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
- recalculateSourceTileCacheSize();
-}
-
-void NativeMapView::resizeFramebuffer(jni::JNIEnv&, int w, int h) {
- fbWidth = w;
- fbHeight = h;
- framebufferSizeChanged = true;
- invalidate();
}
jni::String NativeMapView::getStyleUrl(jni::JNIEnv& env) {
@@ -379,7 +215,7 @@ void NativeMapView::moveBy(jni::JNIEnv&, jni::jdouble dx, jni::jdouble dy, jni::
mbgl::AnimationOptions animationOptions;
if (duration > 0) {
animationOptions.duration.emplace(mbgl::Milliseconds(duration));
- animationOptions.easing.emplace(mbgl::util::UnitBezier { 0, 0.3, 0.6, 1.0 });
+ animationOptions.easing.emplace(mbgl::util::UnitBezier {0.25, 0.46, 0.45, 0.94});
}
map->moveBy({dx, dy}, animationOptions);
}
@@ -456,6 +292,11 @@ jni::Object<CameraPosition> NativeMapView::getCameraForLatLngBounds(jni::JNIEnv&
return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), insets));
}
+jni::Object<CameraPosition> NativeMapView::getCameraForGeometry(jni::JNIEnv& env, jni::Object<geojson::Geometry> jGeometry, double bearing) {
+ auto geometry = geojson::Geometry::convert(env, jGeometry);
+ return CameraPosition::New(env, map->cameraForGeometry(geometry, insets, bearing));
+}
+
void NativeMapView::setReachability(jni::JNIEnv&, jni::jboolean reachable) {
if (reachable) {
mbgl::NetworkStatus::Reachable();
@@ -488,7 +329,6 @@ void NativeMapView::resetZoom(jni::JNIEnv&) {
void NativeMapView::setMinZoom(jni::JNIEnv&, jni::jdouble zoom) {
map->setMinZoom(zoom);
- recalculateSourceTileCacheSize();
}
jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) {
@@ -497,7 +337,6 @@ jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) {
void NativeMapView::setMaxZoom(jni::JNIEnv&, jni::jdouble zoom) {
map->setMaxZoom(zoom);
- recalculateSourceTileCacheSize();
}
jni::jdouble NativeMapView::getMaxZoom(jni::JNIEnv&) {
@@ -562,11 +401,15 @@ void NativeMapView::setContentPadding(JNIEnv&, double top, double left, double b
}
void NativeMapView::scheduleSnapshot(jni::JNIEnv&) {
- snapshot = true;
-}
+ mapRenderer.requestSnapshot([&](PremultipliedImage image) {
+ auto _env = android::AttachEnv();
+ // Convert image to bitmap
+ auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image));
-void NativeMapView::enableFps(jni::JNIEnv&, jni::jboolean enable) {
- fpsEnabled = enable;
+ // invoke Mapview#OnSnapshotReady
+ static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
+ javaPeer->Call(*_env, onSnapshotReady, bitmap);
+ });
}
jni::Object<CameraPosition> NativeMapView::getCameraPosition(jni::JNIEnv& env) {
@@ -607,7 +450,7 @@ jni::Array<jni::jlong> NativeMapView::addMarkers(jni::JNIEnv& env, jni::Array<jn
}
void NativeMapView::onLowMemory(JNIEnv&) {
- map->onLowMemory();
+ rendererFrontend->onLowMemory();
}
using DebugOptions = mbgl::MapDebugOptions;
@@ -616,12 +459,10 @@ void NativeMapView::setDebug(JNIEnv&, jni::jboolean debug) {
DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision
: DebugOptions::NoDebug;
map->setDebug(debugOptions);
- fpsEnabled = debug;
}
void NativeMapView::cycleDebugOptions(JNIEnv&) {
map->cycleDebugOptions();
- fpsEnabled = map->getDebug() != DebugOptions::NoDebug;
}
jni::jboolean NativeMapView::getDebug(JNIEnv&) {
@@ -741,6 +582,11 @@ void NativeMapView::addAnnotationIcon(JNIEnv& env, jni::String symbol, jint w, j
symbolName, std::move(premultipliedImage), float(scale)));
}
+void NativeMapView::removeAnnotationIcon(JNIEnv& env, jni::String symbol) {
+ const std::string symbolName = jni::Make<std::string>(env, symbol);
+ map->removeAnnotationImage(symbolName);
+}
+
jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, jni::String symbolName) {
return map->getTopOffsetPixelsForAnnotationImage(jni::Make<std::string>(env, symbolName));
}
@@ -778,7 +624,27 @@ jni::Array<jlong> NativeMapView::queryPointAnnotations(JNIEnv& env, jni::Object<
};
// Assume only points for now
- mbgl::AnnotationIDs ids = map->queryPointAnnotations(box);
+ mbgl::AnnotationIDs ids = rendererFrontend->queryPointAnnotations(box);
+
+ // Convert result
+ std::vector<jlong> longIds(ids.begin(), ids.end());
+ auto result = jni::Array<jni::jlong>::New(env, ids.size());
+ result.SetRegion<std::vector<jni::jlong>>(env, 0, longIds);
+
+ return result;
+}
+
+jni::Array<jlong> NativeMapView::queryShapeAnnotations(JNIEnv &env, jni::Object<RectF> rect) {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ // Convert input
+ mbgl::ScreenBox box = {
+ {RectF::getLeft(env, rect), RectF::getTop(env, rect)},
+ {RectF::getRight(env, rect), RectF::getBottom(env, rect)},
+ };
+
+ mbgl::AnnotationIDs ids = rendererFrontend->queryShapeAnnotations(box);
// Convert result
std::vector<jlong> longIds(ids.begin(), ids.end());
@@ -800,7 +666,9 @@ jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesFo
}
mapbox::geometry::point<double> point = {x, y};
- return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(point, { layers, toFilter(env, jfilter) }));
+ return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(
+ env,
+ rendererFrontend->queryRenderedFeatures(point, { layers, toFilter(env, jfilter) }));
}
jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesForBox(JNIEnv& env, jni::jfloat left, jni::jfloat top,
@@ -818,7 +686,9 @@ jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesFo
mapbox::geometry::point<double>{ right, bottom }
};
- return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) }));
+ return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(
+ env,
+ rendererFrontend->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) }));
}
jni::Object<Light> NativeMapView::getLight(JNIEnv& env) {
@@ -986,9 +856,7 @@ jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) {
jni::Array<jni::Object<Source>> jSources = jni::Array<jni::Object<Source>>::New(env, sources.size(), Source::javaClass);
int index = 0;
for (auto source : sources) {
- auto jSource = jni::Object<Source>(createJavaSourcePeer(env, *map, *source));
- jSources.Set(env, index, jSource);
- jni::DeleteLocalRef(env, jSource);
+ jSources.Set(env, index, Source::peerForCoreSource(env, *source, *rendererFrontend));
index++;
}
@@ -1004,37 +872,25 @@ jni::Object<Source> NativeMapView::getSource(JNIEnv& env, jni::String sourceId)
}
// Create and return the source's native peer
- return jni::Object<Source>(createJavaSourcePeer(env, *map, *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->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, *map, *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) {
@@ -1054,411 +910,37 @@ void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::jint w, jni::ji
float(scale)));
}
-void NativeMapView::removeImage(JNIEnv& env, jni::String name) {
- map->getStyle().removeImage(jni::Make<std::string>(env, name));
-}
-
-// Private methods //
-
-void NativeMapView::_initializeDisplay() {
- assert(display == EGL_NO_DISPLAY);
- assert(config == nullptr);
- assert(format < 0);
-
- display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (display == EGL_NO_DISPLAY) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError());
- throw std::runtime_error("eglGetDisplay() failed");
- }
-
- EGLint major, minor;
- if (!eglInitialize(display, &major, &minor)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError());
- throw std::runtime_error("eglInitialize() failed");
- }
- if ((major <= 1) && (minor < 3)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major,
- minor);
- throw std::runtime_error("EGL version is too low");
- }
-
- // Detect if we are in emulator.
- const bool inEmulator = []() {
- char prop[PROP_VALUE_MAX];
- __system_property_get("ro.kernel.qemu", prop);
- return strtol(prop, nullptr, 0) == 1;
- }();
-
- if (inEmulator) {
- // XXX https://code.google.com/p/android/issues/detail?id=78977
- mbgl::Log::Warning(mbgl::Event::Android, "Running SDK in emulator!");
- }
-
- 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");
- }
-
- // Get all configs at least RGB 565 with 16 depth and 8 stencil
- EGLint configAttribs[] = {
- EGL_CONFIG_CAVEAT, EGL_NONE,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_BUFFER_SIZE, 16,
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 6,
- EGL_BLUE_SIZE, 5,
- EGL_DEPTH_SIZE, 16,
- EGL_STENCIL_SIZE, 8,
- (inEmulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT,
- (inEmulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER,
- EGL_NONE
- };
-
- EGLint numConfigs;
- if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d",
- eglGetError());
- throw std::runtime_error("eglChooseConfig() failed");
- }
- if (numConfigs < 1) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs.");
- throw std::runtime_error("eglChooseConfig() failed");
- }
-
- const auto configs = std::make_unique<EGLConfig[]>(numConfigs);
- if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError());
- throw std::runtime_error("eglChooseConfig() failed");
- }
-
- config = chooseConfig(configs.get(), numConfigs);
- if (config == nullptr) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen");
- throw std::runtime_error("No config chosen");
- }
-
- if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-}
-
-// Quality
-typedef enum {
- Format16Bit = 3,
- Format32BitNoAlpha = 1,
- Format32BitAlpha = 2,
- Format24Bit = 0,
- Unknown = 4
-} BufferFormat;
-
-typedef enum {
- Format16Depth8Stencil = 1,
- Format24Depth8Stencil = 0,
-} DepthStencilFormat;
-
-// Tuple is <buffer_format, depth_stencil_format, is_not_conformant, is_caveat, config_num,
-// config_id>
-typedef std::tuple<BufferFormat, DepthStencilFormat, bool, bool, int, EGLConfig> ConfigProperties;
-
-EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfigs) {
- // Create a list of configs that pass our filters
- std::list<ConfigProperties> configList;
- for (int i = 0; i < numConfigs; i++) {
- EGLint caveat, conformant, bits, red, green, blue, alpha, alphaMask, depth, stencil,
- sampleBuffers, samples;
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d",
- eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError());
- throw std::runtime_error("eglGetConfigAttrib() failed");
- }
-
- bool configOk = true;
- configOk &= (depth == 24) || (depth == 16);
- configOk &= stencil == 8;
- configOk &= sampleBuffers == 0;
- configOk &= samples == 0;
-
- // Filter our configs first for depth, stencil and anti-aliasing
- if (configOk) {
- // Work out the config's buffer format
- BufferFormat bufferFormat;
- if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) {
- bufferFormat = Format16Bit;
- } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
- bufferFormat = Format32BitNoAlpha;
- } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
- bufferFormat = Format32BitAlpha;
- } else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
- bufferFormat = Format24Bit;
- } else {
- bufferFormat = Unknown;
- }
-
- // Work out the config's depth stencil format
- DepthStencilFormat depthStencilFormat;
- if ((depth == 16) && (stencil == 8)) {
- depthStencilFormat = Format16Depth8Stencil;
- } else {
- depthStencilFormat = Format24Depth8Stencil;
- }
-
- bool isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
- bool isCaveat = caveat != EGL_NONE;
- EGLConfig configId = configs[i];
-
- // Ignore formats we don't recognise
- if (bufferFormat != Unknown) {
- configList.push_back(std::make_tuple(bufferFormat, depthStencilFormat,
- isNotConformant, isCaveat, i, configId));
- }
- }
- }
-
- if (configList.empty()) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "Config list was empty.");
- }
-
- // Sort the configs to find the best one
- configList.sort();
- bool isConformant = !std::get<2>(configList.front());
- bool isCaveat = std::get<3>(configList.front());
- EGLConfig configId = std::get<5>(configList.front());
+void NativeMapView::addImages(JNIEnv& env, jni::Array<jni::Object<mbgl::android::Image>> jimages) {
+ jni::NullCheck(env, &jimages);
+ std::size_t len = jimages.Length(env);
- if (isCaveat) {
- mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat.");
- }
- if (!isConformant) {
- mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant.");
- }
-
- return configId;
-}
-
-void NativeMapView::_terminateDisplay() {
- if (display != EGL_NO_DISPLAY) {
- // Destroy the surface first, if it still exists. This call needs a valid surface.
- if (surface != EGL_NO_SURFACE) {
- if (!eglDestroySurface(display, surface)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
- eglGetError());
- throw std::runtime_error("eglDestroySurface() failed");
- }
- surface = EGL_NO_SURFACE;
- }
-
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
-
- if (!eglTerminate(display)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d",
- eglGetError());
- throw std::runtime_error("eglTerminate() failed");
- }
- }
-
- display = EGL_NO_DISPLAY;
- config = nullptr;
- format = -1;
-}
-
-void NativeMapView::_initializeContext() {
- assert(display != EGL_NO_DISPLAY);
- assert(context == EGL_NO_CONTEXT);
- assert(config != nullptr);
-
- const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
- context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
- if (context == EGL_NO_CONTEXT) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d",
- eglGetError());
- throw std::runtime_error("eglCreateContext() failed");
- }
-}
-
-void NativeMapView::_terminateContext() {
- if (display != EGL_NO_DISPLAY) {
-
- if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
- mbgl::Log::Error(mbgl::Event::OpenGL,
- "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
-
- if (context != EGL_NO_CONTEXT) {
- if (!eglDestroyContext(display, context)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d",
- eglGetError());
- throw std::runtime_error("eglDestroyContext() failed");
- }
- }
+ for (std::size_t i = 0; i < len; i++) {
+ jni::Object<mbgl::android::Image> jimage = jimages.Get(env, i);
+ auto image = mbgl::android::Image::getImage(env, jimage);
+ map->getStyle().addImage(std::make_unique<mbgl::style::Image>(image));
+ jni::DeleteLocalRef(env, jimage);
}
-
- context = EGL_NO_CONTEXT;
}
-void NativeMapView::_createSurface(ANativeWindow *window_) {
- assert(window == nullptr);
- assert(window_ != nullptr);
- window = window_;
-
- assert(display != EGL_NO_DISPLAY);
- assert(surface == EGL_NO_SURFACE);
- assert(config != nullptr);
- assert(format >= 0);
-
- ANativeWindow_setBuffersGeometry(window, 0, 0, format);
-
- const EGLint surfaceAttribs[] = {EGL_NONE};
- surface = eglCreateWindowSurface(display, config, window, surfaceAttribs);
- if (surface == EGL_NO_SURFACE) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d",
- eglGetError());
- throw std::runtime_error("eglCreateWindowSurface() failed");
- }
-
- if (firstRender) {
- firstRender = false;
-
- BackendScope guard(*this);
-
- if (!eglMakeCurrent(display, surface, surface, context)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
- eglGetError());
- throw std::runtime_error("eglMakeCurrent() failed");
- }
- }
+void NativeMapView::removeImage(JNIEnv& env, jni::String name) {
+ map->getStyle().removeImage(jni::Make<std::string>(env, name));
}
-void NativeMapView::_destroySurface() {
- if (surface != EGL_NO_SURFACE) {
- if (!eglDestroySurface(display, surface)) {
- mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
- eglGetError());
- throw std::runtime_error("eglDestroySurface() failed");
- }
- }
-
- surface = EGL_NO_SURFACE;
- firstRender = true;
-
- if (window != nullptr) {
- ANativeWindow_release(window);
- window = nullptr;
+jni::Object<Bitmap> NativeMapView::getImage(JNIEnv& env, jni::String name) {
+ const mbgl::style::Image *image = map->getStyle().getImage(jni::Make<std::string>(env, name));
+ if (image) {
+ return Bitmap::CreateBitmap(env, image->getImage());
+ } else {
+ return jni::Object<Bitmap>();
}
}
-mbgl::Size NativeMapView::getFramebufferSize() const {
- return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) };
+void NativeMapView::setPrefetchesTiles(JNIEnv&, jni::jboolean enable) {
+ map->setPrefetchZoomDelta(enable ? util::DEFAULT_PREFETCH_ZOOM_DELTA : uint8_t(0));
}
-void NativeMapView::updateAssumedState() {
- assumeFramebufferBinding(0);
- assumeViewport(0, 0, getFramebufferSize());
-}
-
-void NativeMapView::updateFps() {
- if (!fpsEnabled) {
- return;
- }
-
- static int frames = 0;
- static int64_t timeElapsed = 0LL;
-
- frames++;
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- int64_t currentTime = now.tv_sec * 1000000000LL + now.tv_nsec;
-
- if (currentTime - timeElapsed >= 1) {
- fps = frames / ((currentTime - timeElapsed) / 1E9);
- mbgl::Log::Info(mbgl::Event::Render, "FPS: %4.2f", fps);
- timeElapsed = currentTime;
- frames = 0;
- }
-
- assert(vm != nullptr);
-
- android::UniqueEnv _env = android::AttachEnv();
- static auto onFpsChanged = javaClass.GetMethod<void (double)>(*_env, "onFpsChanged");
- javaPeer->Call(*_env, onFpsChanged, fps);
+jni::jboolean NativeMapView::getPrefetchesTiles(JNIEnv&) {
+ return jni::jboolean(map->getPrefetchZoomDelta() > 0);
}
// Static methods //
@@ -1473,19 +955,10 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
// Register the peer
jni::RegisterNativePeer<NativeMapView>(env, NativeMapView::javaClass, "nativePtr",
- std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::String, jni::jint, jni::jlong>,
+ std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::Object<MapRenderer>, jni::jfloat>,
"nativeInitialize",
"nativeDestroy",
- METHOD(&NativeMapView::render, "nativeRender"),
- METHOD(&NativeMapView::update, "nativeUpdate"),
METHOD(&NativeMapView::resizeView, "nativeResizeView"),
- METHOD(&NativeMapView::resizeFramebuffer, "nativeResizeFramebuffer"),
- METHOD(&NativeMapView::initializeDisplay, "nativeInitializeDisplay"),
- METHOD(&NativeMapView::terminateDisplay, "nativeTerminateDisplay"),
- METHOD(&NativeMapView::initializeContext, "nativeInitializeContext"),
- METHOD(&NativeMapView::terminateContext, "nativeTerminateContext"),
- METHOD(&NativeMapView::createSurface, "nativeCreateSurface"),
- METHOD(&NativeMapView::destroySurface, "nativeDestroySurface"),
METHOD(&NativeMapView::getStyleUrl, "nativeGetStyleUrl"),
METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"),
METHOD(&NativeMapView::getStyleJson, "nativeGetStyleJson"),
@@ -1499,6 +972,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::getLatLng, "nativeGetLatLng"),
METHOD(&NativeMapView::setLatLng, "nativeSetLatLng"),
METHOD(&NativeMapView::getCameraForLatLngBounds, "nativeGetCameraForLatLngBounds"),
+ METHOD(&NativeMapView::getCameraForGeometry, "nativeGetCameraForGeometry"),
METHOD(&NativeMapView::setReachability, "nativeSetReachability"),
METHOD(&NativeMapView::resetPosition, "nativeResetPosition"),
METHOD(&NativeMapView::getPitch, "nativeGetPitch"),
@@ -1518,7 +992,6 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"),
METHOD(&NativeMapView::setContentPadding, "nativeSetContentPadding"),
METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"),
- METHOD(&NativeMapView::enableFps, "nativeSetEnableFps"),
METHOD(&NativeMapView::getCameraPosition, "nativeGetCameraPosition"),
METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"),
METHOD(&NativeMapView::addMarkers, "nativeAddMarkers"),
@@ -1538,12 +1011,14 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
METHOD(&NativeMapView::updatePolygon, "nativeUpdatePolygon"),
METHOD(&NativeMapView::removeAnnotations, "nativeRemoveAnnotations"),
METHOD(&NativeMapView::addAnnotationIcon, "nativeAddAnnotationIcon"),
+ METHOD(&NativeMapView::removeAnnotationIcon, "nativeRemoveAnnotationIcon"),
METHOD(&NativeMapView::getTopOffsetPixelsForAnnotationSymbol, "nativeGetTopOffsetPixelsForAnnotationSymbol"),
METHOD(&NativeMapView::getTransitionDuration, "nativeGetTransitionDuration"),
METHOD(&NativeMapView::setTransitionDuration, "nativeSetTransitionDuration"),
METHOD(&NativeMapView::getTransitionDelay, "nativeGetTransitionDelay"),
METHOD(&NativeMapView::setTransitionDelay, "nativeSetTransitionDelay"),
METHOD(&NativeMapView::queryPointAnnotations, "nativeQueryPointAnnotations"),
+ METHOD(&NativeMapView::queryShapeAnnotations, "nativeQueryShapeAnnotations"),
METHOD(&NativeMapView::queryRenderedFeaturesForPoint, "nativeQueryRenderedFeaturesForPoint"),
METHOD(&NativeMapView::queryRenderedFeaturesForBox, "nativeQueryRenderedFeaturesForBox"),
METHOD(&NativeMapView::getLight, "nativeGetLight"),
@@ -1558,13 +1033,16 @@ 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"),
METHOD(&NativeMapView::removeImage, "nativeRemoveImage"),
- METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds")
+ METHOD(&NativeMapView::getImage, "nativeGetImage"),
+ METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds"),
+ METHOD(&NativeMapView::setPrefetchesTiles, "nativeSetPrefetchesTiles"),
+ METHOD(&NativeMapView::getPrefetchesTiles, "nativeGetPrefetchesTiles")
);
}
-}
-}
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index df43ad08b7..507d77ac5f 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -1,30 +1,30 @@
#pragma once
-#include <mbgl/map/backend.hpp>
#include <mbgl/map/change.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/view.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
-#include "file_source.hpp"
#include "annotation/marker.hpp"
#include "annotation/polygon.hpp"
#include "annotation/polyline.hpp"
#include "graphics/pointf.hpp"
#include "graphics/rectf.hpp"
#include "geojson/feature.hpp"
+#include "geojson/geometry.hpp"
#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"
#include "style/light.hpp"
+#include "bitmap.hpp"
#include <exception>
#include <string>
@@ -36,7 +36,11 @@
namespace mbgl {
namespace android {
-class NativeMapView : public View, public Backend {
+class AndroidRendererFrontend;
+class FileSource;
+class MapRenderer;
+
+class NativeMapView : public MapObserver {
public:
static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/NativeMapView"; };
@@ -48,26 +52,15 @@ public:
NativeMapView(jni::JNIEnv&,
jni::Object<NativeMapView>,
jni::Object<FileSource>,
- jni::jfloat pixelRatio,
- jni::String programCacheDir,
- jni::jint availableProcessors,
- jni::jlong totalMemory);
+ jni::Object<MapRenderer>,
+ jni::jfloat pixelRatio);
virtual ~NativeMapView();
- // mbgl::View //
-
- void bind() override;
-
- // mbgl::Backend //
-
- void updateAssumedState() override;
- void invalidate() override;
-
// Deprecated //
void notifyMapChange(mbgl::MapChange);
- // mbgl::Backend (mbgl::MapObserver) //
+ // mbgl::RendererBackend (mbgl::MapObserver) //
void onCameraWillChange(MapObserver::CameraChangeMode) override;
void onCameraIsChanging() override;
void onCameraDidChange(MapObserver::CameraChangeMode) override;
@@ -83,28 +76,8 @@ public:
// JNI //
- void destroy(jni::JNIEnv&);
-
- void render(jni::JNIEnv&);
-
- void update(jni::JNIEnv&);
-
void resizeView(jni::JNIEnv&, int, int);
- void resizeFramebuffer(jni::JNIEnv&, int, int);
-
- void initializeDisplay(jni::JNIEnv&);
-
- void terminateDisplay(jni::JNIEnv&);
-
- void initializeContext(jni::JNIEnv&);
-
- void terminateContext(jni::JNIEnv&);
-
- void createSurface(jni::JNIEnv&, jni::Object<>);
-
- void destroySurface(jni::JNIEnv&);
-
jni::String getStyleUrl(jni::JNIEnv&);
void setStyleUrl(jni::JNIEnv&, jni::String);
@@ -133,6 +106,8 @@ public:
jni::Object<CameraPosition> getCameraForLatLngBounds(jni::JNIEnv&, jni::Object<mbgl::android::LatLngBounds>);
+ jni::Object<CameraPosition> getCameraForGeometry(jni::JNIEnv&, jni::Object<geojson::Geometry>, double bearing);
+
void setReachability(jni::JNIEnv&, jni::jboolean);
void resetPosition(jni::JNIEnv&);
@@ -171,8 +146,6 @@ public:
void scheduleSnapshot(jni::JNIEnv&);
- void enableFps(jni::JNIEnv&, jni::jboolean enable);
-
jni::Object<CameraPosition> getCameraPosition(jni::JNIEnv&);
void updateMarker(jni::JNIEnv&, jni::jlong, jni::jdouble, jni::jdouble, jni::String);
@@ -211,6 +184,8 @@ public:
void addAnnotationIcon(JNIEnv&, jni::String, jint, jint, jfloat, jni::Array<jbyte>);
+ void removeAnnotationIcon(JNIEnv&, jni::String);
+
jni::jdouble getTopOffsetPixelsForAnnotationSymbol(JNIEnv&, jni::String);
jni::jlong getTransitionDuration(JNIEnv&);
@@ -223,6 +198,8 @@ public:
jni::Array<jlong> queryPointAnnotations(JNIEnv&, jni::Object<RectF>);
+ jni::Array<jlong> queryShapeAnnotations(JNIEnv&, jni::Object<RectF>);
+
jni::Array<jni::Object<geojson::Feature>> queryRenderedFeaturesForPoint(JNIEnv&, jni::jfloat, jni::jfloat,
jni::Array<jni::String>,
jni::Array<jni::Object<>> jfilter);
@@ -253,89 +230,45 @@ 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 removeImage(JNIEnv&, jni::String);
-
-protected:
- // mbgl::Backend //
-
- gl::ProcAddress initializeExtension(const char*) override;
- void activate() override;
- void deactivate() override;
-
-private:
- void _initializeDisplay();
+ void addImages(JNIEnv&, jni::Array<jni::Object<mbgl::android::Image>>);
- void _terminateDisplay();
-
- void _initializeContext();
-
- void _terminateContext();
-
- void _createSurface(ANativeWindow*);
-
- void _destroySurface();
+ void removeImage(JNIEnv&, jni::String);
- EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
+ jni::Object<Bitmap> getImage(JNIEnv&, jni::String);
- mbgl::Size getFramebufferSize() const;
+ void setPrefetchesTiles(JNIEnv&, jni::jboolean);
- void updateFps();
+ jni::jboolean getPrefetchesTiles(JNIEnv&);
private:
- void recalculateSourceTileCacheSize();
+ std::unique_ptr<AndroidRendererFrontend> rendererFrontend;
JavaVM *vm = nullptr;
jni::UniqueWeakObject<NativeMapView> javaPeer;
+ MapRenderer& mapRenderer;
+
std::string styleUrl;
std::string apiKey;
- ANativeWindow *window = nullptr;
-
- EGLConfig config = nullptr;
- EGLint format = -1;
-
- EGLDisplay oldDisplay = EGL_NO_DISPLAY;
- EGLSurface oldReadSurface = EGL_NO_SURFACE;
- EGLSurface oldDrawSurface = EGL_NO_SURFACE;
- EGLContext oldContext = EGL_NO_CONTEXT;
-
- EGLDisplay display = EGL_NO_DISPLAY;
- EGLSurface surface = EGL_NO_SURFACE;
- EGLContext context = EGL_NO_CONTEXT;
-
-
float pixelRatio;
- bool fpsEnabled = false;
- bool snapshot = false;
- bool firstRender = true;
- double fps = 0.0;
// Minimum texture size according to OpenGL ES 2.0 specification.
int width = 64;
int height = 64;
- int fbWidth = 64;
- int fbHeight = 64;
-
- bool framebufferSizeChanged = true;
-
- int availableProcessors = 0;
- size_t totalMemory = 0;
// Ensure these are initialised last
std::shared_ptr<mbgl::ThreadPool> threadPool;
std::unique_ptr<mbgl::Map> map;
mbgl::EdgeInsets insets;
-
- unsigned active = 0;
};
} // namespace android
diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp
index 3c605b70e8..1d284a9e72 100644
--- a/platform/android/src/run_loop.cpp
+++ b/platform/android/src/run_loop.cpp
@@ -4,6 +4,7 @@
#include <mbgl/util/thread_local.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/timer.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <android/looper.h>
@@ -23,7 +24,6 @@
namespace {
using namespace mbgl::util;
-static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
int looperCallbackNew(int fd, int, void* data) {
int buffer[1];
@@ -200,24 +200,27 @@ Milliseconds RunLoop::Impl::processRunnables() {
}
RunLoop* RunLoop::Get() {
- return current.get();
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
}
RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>(this, type)) {
- current.set(this);
+ Scheduler::SetCurrent(this);
}
RunLoop::~RunLoop() {
- current.set(nullptr);
+ Scheduler::SetCurrent(nullptr);
}
LOOP_HANDLE RunLoop::getLoopHandle() {
- return current.get()->impl.get();
+ return Get()->impl.get();
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
- withMutex([&] { queue.push(std::move(task)); });
- impl->wake();
+ withMutex([&] {
+ queue.push(std::move(task));
+ impl->wake();
+ });
}
void RunLoop::run() {
diff --git a/platform/android/src/snapshotter/map_snapshot.cpp b/platform/android/src/snapshotter/map_snapshot.cpp
new file mode 100644
index 0000000000..f5092b3c56
--- /dev/null
+++ b/platform/android/src/snapshotter/map_snapshot.cpp
@@ -0,0 +1,60 @@
+#include "map_snapshot.hpp"
+
+#include "../bitmap.hpp"
+#include "../jni/collection.hpp"
+
+#include <memory>
+
+namespace mbgl {
+namespace android {
+
+MapSnapshot::MapSnapshot(float pixelRatio_, MapSnapshot::PointForFn pointForFn_)
+ : pixelRatio(pixelRatio_)
+ , pointForFn(std::move(pointForFn_)) {
+}
+
+MapSnapshot::~MapSnapshot() = default;
+
+jni::Object<PointF> MapSnapshot::pixelForLatLng(jni::JNIEnv& env, jni::Object<LatLng> jLatLng) {
+ ScreenCoordinate point = pointForFn(LatLng::getLatLng(env, jLatLng));
+ return PointF::New(env, point.x * pixelRatio, point.y * pixelRatio);
+}
+
+
+// Static methods //
+
+jni::Object<MapSnapshot> MapSnapshot::New(JNIEnv& env,
+ PremultipliedImage&& image,
+ float pixelRatio,
+ std::vector<std::string> attributions,
+ bool showLogo,
+ mbgl::MapSnapshotter::PointForFn pointForFn) {
+ // Create the bitmap
+ auto bitmap = Bitmap::CreateBitmap(env, std::move(image));
+
+ // Create the Mapsnapshot peers
+ static auto constructor = javaClass.GetConstructor<jni::jlong, jni::Object<Bitmap>, jni::Array<jni::String>, jni::jboolean>(env);
+ auto nativePeer = std::make_unique<MapSnapshot>(pixelRatio, pointForFn);
+ return javaClass.New(env, constructor, reinterpret_cast<jlong>(nativePeer.release()), bitmap, jni::Make<jni::Array<jni::String>>(env, attributions), (jni::jboolean) showLogo);
+}
+
+jni::Class<MapSnapshot> MapSnapshot::javaClass;
+
+void MapSnapshot::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ MapSnapshot::javaClass = *jni::Class<MapSnapshot>::Find(env).NewGlobalRef(env).release();
+
+#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<MapSnapshot>(env, MapSnapshot::javaClass,
+ "nativePtr",
+ std::make_unique<MapSnapshot, JNIEnv&>,
+ "initialize",
+ "finalize",
+ METHOD(&MapSnapshot::pixelForLatLng, "pixelForLatLng")
+ );
+}
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/snapshotter/map_snapshot.hpp b/platform/android/src/snapshotter/map_snapshot.hpp
new file mode 100644
index 0000000000..4673dcd16e
--- /dev/null
+++ b/platform/android/src/snapshotter/map_snapshot.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <mbgl/map/map_snapshotter.hpp>
+
+#include <jni/jni.hpp>
+
+#include "../geometry/lat_lng.hpp"
+#include "../graphics/pointf.hpp"
+
+#include <vector>
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+class MapSnapshot {
+public:
+
+ using PointForFn = mbgl::MapSnapshotter::PointForFn;
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/snapshotter/MapSnapshot"; };
+
+ static void registerNative(jni::JNIEnv&);
+
+ static jni::Object<MapSnapshot> New(JNIEnv& env,
+ PremultipliedImage&& image,
+ float pixelRatio,
+ std::vector<std::string> attributions,
+ bool showLogo,
+ PointForFn pointForFn);
+
+ MapSnapshot(jni::JNIEnv&) {};
+ MapSnapshot(float pixelRatio, PointForFn);
+ ~MapSnapshot();
+
+ jni::Object<PointF> pixelForLatLng(jni::JNIEnv&, jni::Object<LatLng>);
+
+private:
+ static jni::Class<MapSnapshot> javaClass;
+
+ float pixelRatio;
+ mbgl::MapSnapshotter::PointForFn pointForFn;
+};
+
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/snapshotter/map_snapshotter.cpp b/platform/android/src/snapshotter/map_snapshotter.cpp
new file mode 100644
index 0000000000..71f8b4f4c0
--- /dev/null
+++ b/platform/android/src/snapshotter/map_snapshotter.cpp
@@ -0,0 +1,155 @@
+#include "map_snapshotter.hpp"
+
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+#include "../attach_env.hpp"
+#include "map_snapshot.hpp"
+
+namespace mbgl {
+namespace android {
+
+MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env,
+ jni::Object<MapSnapshotter> _obj,
+ jni::Object<FileSource> _jFileSource,
+ jni::jfloat _pixelRatio,
+ jni::jint width,
+ jni::jint height,
+ jni::String styleURL,
+ jni::Object<LatLngBounds> region,
+ jni::Object<CameraPosition> position,
+ jni::jboolean _showLogo,
+ jni::String _programCacheDir)
+ : javaPeer(SeizeGenericWeakRef(_env, jni::Object<MapSnapshotter>(jni::NewWeakGlobalRef(_env, _obj.Get()).release())))
+ , pixelRatio(_pixelRatio)
+ , threadPool(sharedThreadPool()) {
+
+ // Get a reference to the JavaVM for callbacks
+ if (_env.GetJavaVM(&vm) < 0) {
+ _env.ExceptionDescribe();
+ return;
+ }
+
+ jFileSource = FileSource::getNativePeer(_env, _jFileSource);
+ auto& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, _jFileSource);
+ auto size = mbgl::Size { static_cast<uint32_t>(width), static_cast<uint32_t>(height) };
+ auto cameraOptions = position ? CameraPosition::getCameraOptions(_env, position) : CameraOptions();
+ optional<mbgl::LatLngBounds> bounds;
+ if (region) {
+ bounds = LatLngBounds::getLatLngBounds(_env, region);
+ }
+
+ showLogo = _showLogo;
+ // Create the core snapshotter
+ snapshotter = std::make_unique<mbgl::MapSnapshotter>(fileSource,
+ *threadPool,
+ jni::Make<std::string>(_env, styleURL),
+ size,
+ pixelRatio,
+ cameraOptions,
+ bounds,
+ jni::Make<std::string>(_env, _programCacheDir));
+
+}
+
+MapSnapshotter::~MapSnapshotter() = default;
+
+void MapSnapshotter::start(JNIEnv& env) {
+ MBGL_VERIFY_THREAD(tid);
+ activateFilesource(env);
+
+ snapshotCallback = std::make_unique<Actor<mbgl::MapSnapshotter::Callback>>(
+ *Scheduler::GetCurrent(),
+ [this](std::exception_ptr err, PremultipliedImage image, std::vector<std::string> attributions, mbgl::MapSnapshotter::PointForFn pointForFn) {
+ MBGL_VERIFY_THREAD(tid);
+ android::UniqueEnv _env = android::AttachEnv();
+
+ if (err) {
+ // error handler callback
+ static auto onSnapshotFailed = javaClass.GetMethod<void (jni::String)>(*_env, "onSnapshotFailed");
+ javaPeer->Call(*_env, onSnapshotFailed, jni::Make<jni::String>(*_env, util::toString(err)));
+ } else {
+ // Create the wrapper
+ auto mapSnapshot = android::MapSnapshot::New(*_env, std::move(image), pixelRatio, attributions, showLogo, pointForFn);
+
+ // invoke callback
+ static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<MapSnapshot>)>(*_env, "onSnapshotReady");
+ javaPeer->Call(*_env, onSnapshotReady, mapSnapshot);
+ }
+
+ deactivateFilesource(*_env);
+ });
+
+ snapshotter->snapshot(snapshotCallback->self());
+}
+
+void MapSnapshotter::cancel(JNIEnv& env) {
+ MBGL_VERIFY_THREAD(tid);
+ snapshotCallback.reset();
+ deactivateFilesource(env);
+}
+
+void MapSnapshotter::setStyleUrl(JNIEnv& env, jni::String styleURL) {
+ snapshotter->setStyleURL(jni::Make<std::string>(env, styleURL));
+}
+
+void MapSnapshotter::setSize(JNIEnv&, jni::jint width, jni::jint height) {
+ auto size = mbgl::Size { static_cast<uint32_t>(width), static_cast<uint32_t>(height) };
+ snapshotter->setSize(size);
+}
+
+void MapSnapshotter::setCameraPosition(JNIEnv& env, jni::Object<CameraPosition> position) {
+ auto options = CameraPosition::getCameraOptions(env, position);
+ snapshotter->setCameraOptions(options);
+}
+
+void MapSnapshotter::setRegion(JNIEnv& env, jni::Object<LatLngBounds> region) {
+ snapshotter->setRegion(LatLngBounds::getLatLngBounds(env, region));
+}
+
+// Private methods //
+
+void MapSnapshotter::activateFilesource(JNIEnv& env) {
+ if (!activatedFilesource) {
+ activatedFilesource = true;
+ jFileSource->resume(env);
+ }
+}
+
+void MapSnapshotter::deactivateFilesource(JNIEnv& env) {
+ if (activatedFilesource) {
+ activatedFilesource = false;
+ jFileSource->pause(env);
+ }
+}
+
+// Static methods //
+
+jni::Class<MapSnapshotter> MapSnapshotter::javaClass;
+
+void MapSnapshotter::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ MapSnapshotter::javaClass = *jni::Class<MapSnapshotter>::Find(env).NewGlobalRef(env).release();
+
+#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ // Register the peer
+ jni::RegisterNativePeer<MapSnapshotter>(env, MapSnapshotter::javaClass, "nativePtr",
+ std::make_unique<MapSnapshotter, JNIEnv&, jni::Object<MapSnapshotter>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jint, jni::String, jni::Object<LatLngBounds>, jni::Object<CameraPosition>, jni::jboolean, jni::String>,
+ "nativeInitialize",
+ "finalize",
+ METHOD(&MapSnapshotter::setStyleUrl, "setStyleUrl"),
+ METHOD(&MapSnapshotter::setSize, "setSize"),
+ METHOD(&MapSnapshotter::setCameraPosition, "setCameraPosition"),
+ METHOD(&MapSnapshotter::setRegion, "setRegion"),
+ METHOD(&MapSnapshotter::start, "nativeStart"),
+ METHOD(&MapSnapshotter::cancel, "nativeCancel")
+ );
+}
+
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/snapshotter/map_snapshotter.hpp b/platform/android/src/snapshotter/map_snapshotter.hpp
new file mode 100644
index 0000000000..4cdf4bcf2b
--- /dev/null
+++ b/platform/android/src/snapshotter/map_snapshotter.hpp
@@ -0,0 +1,76 @@
+#pragma once
+
+#include <mbgl/map/map_snapshotter.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/util.hpp>
+
+#include "../file_source.hpp"
+#include "../geometry/lat_lng_bounds.hpp"
+#include "../map/camera_position.hpp"
+
+#include <jni/jni.hpp>
+#include "../jni/generic_global_ref_deleter.hpp"
+
+#include <memory>
+
+namespace mbgl {
+namespace android {
+
+class SnapshotterRendererFrontend;
+
+class MapSnapshotter {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/snapshotter/MapSnapshotter"; };
+
+ static jni::Class<MapSnapshotter> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ MapSnapshotter(jni::JNIEnv&,
+ jni::Object<MapSnapshotter>,
+ jni::Object<FileSource>,
+ jni::jfloat pixelRatio,
+ jni::jint width,
+ jni::jint height,
+ jni::String styleURL,
+ jni::Object<LatLngBounds> region,
+ jni::Object<CameraPosition> position,
+ jni::jboolean showLogo,
+ jni::String programCacheDir);
+
+ ~MapSnapshotter();
+
+ void setStyleUrl(JNIEnv&, jni::String styleURL);
+
+ void setSize(JNIEnv&, jni::jint width, jni::jint height);
+
+ void setCameraPosition(JNIEnv&, jni::Object<CameraPosition> position);
+
+ void setRegion(JNIEnv&, jni::Object<LatLngBounds> region);
+
+ void start(JNIEnv&);
+
+ void cancel(JNIEnv&);
+
+private:
+ MBGL_STORE_THREAD(tid);
+
+ JavaVM *vm = nullptr;
+ GenericUniqueWeakObject<MapSnapshotter> javaPeer;
+
+ float pixelRatio;
+ bool showLogo;
+
+ std::shared_ptr<mbgl::ThreadPool> threadPool;
+ std::unique_ptr<Actor<mbgl::MapSnapshotter::Callback>> snapshotCallback;
+ std::unique_ptr<mbgl::MapSnapshotter> snapshotter;
+
+ FileSource *jFileSource;
+ void activateFilesource(JNIEnv&);
+ void deactivateFilesource(JNIEnv&);
+ bool activatedFilesource = false;
+};
+
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp
index 082fe411e2..510a9f8444 100644
--- a/platform/android/src/style/android_conversion.hpp
+++ b/platform/android/src/style/android_conversion.hpp
@@ -4,8 +4,9 @@
#include <mbgl/util/feature.hpp>
#include <mbgl/util/logging.hpp>
-#include <mbgl/style/conversion.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
#include <jni/jni.hpp>
@@ -13,89 +14,105 @@ namespace mbgl {
namespace style {
namespace conversion {
-inline bool isUndefined(const mbgl::android::Value& value) {
- return value.isNull();
-}
+template <>
+class ConversionTraits<mbgl::android::Value> {
+public:
+ static bool isUndefined(const mbgl::android::Value& value) {
+ return value.isNull();
+ }
-inline bool isArray(const mbgl::android::Value& value) {
- return value.isArray();
-}
+ static bool isArray(const mbgl::android::Value& value) {
+ return value.isArray();
+ }
-inline bool isObject(const mbgl::android::Value& value) {
- return value.isObject();
-}
+ static bool isObject(const mbgl::android::Value& value) {
+ return value.isObject();
+ }
-inline std::size_t arrayLength(const mbgl::android::Value& value) {
- return value.getLength();;
-}
+ static std::size_t arrayLength(const mbgl::android::Value& value) {
+ return value.getLength();;
+ }
-inline mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) {
- return value.get(i);
-}
+ static mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) {
+ return value.get(i);
+ }
-inline optional<mbgl::android::Value> objectMember(const mbgl::android::Value& value, const char* key) {
- mbgl::android::Value member = value.get(key);
+ static optional<mbgl::android::Value> objectMember(const mbgl::android::Value& value, const char* key) {
+ mbgl::android::Value member = value.get(key);
+ if (!member.isNull()) {
+ return member;
+ } else {
+ return {};
+ }
+ }
- if (!member.isNull()) {
- return member;
- } else {
+ template <class Fn>
+ static optional<Error> eachMember(const mbgl::android::Value&, Fn&&) {
+ // TODO
+ mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented");
return {};
}
-}
-template <class Fn>
-optional<Error> eachMember(const mbgl::android::Value&, Fn&&) {
- // TODO
- mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented");
- return {};
-}
+ static optional<bool> toBool(const mbgl::android::Value& value) {
+ if (value.isBool()) {
+ return value.toBool();
+ } else {
+ return {};
+ }
+ }
-inline optional<bool> toBool(const mbgl::android::Value& value) {
- if (value.isBool()) {
- return value.toBool();
- } else {
- return {};
+ static optional<float> toNumber(const mbgl::android::Value& value) {
+ if (value.isNumber()) {
+ auto num = value.toFloat();
+ return num;
+ } else {
+ return {};
+ }
}
-}
-inline optional<float> toNumber(const mbgl::android::Value& value) {
- if (value.isNumber()) {
- auto num = value.toFloat();
- return num;
- } else {
- return {};
+ static optional<double> toDouble(const mbgl::android::Value& value) {
+ if (value.isNumber()) {
+ return value.toDouble();
+ } else {
+ return {};
+ }
}
-}
-inline optional<double> toDouble(const mbgl::android::Value& value) {
- if (value.isNumber()) {
- return value.toDouble();
- } else {
- return {};
+ static optional<std::string> toString(const mbgl::android::Value& value) {
+ if (value.isString()) {
+ return value.toString();
+ } else {
+ return {};
+ }
}
-}
-inline optional<std::string> toString(const mbgl::android::Value& value) {
- if (value.isString()) {
- return value.toString();
- } else {
- return {};
+ static optional<Value> toValue(const mbgl::android::Value& value) {
+ if (value.isNull()) {
+ return {};
+ } else if (value.isBool()) {
+ return { value.toBool() };
+ } else if (value.isString()) {
+ return { value.toString() };
+ } else if (value.isNumber()) {
+ auto doubleVal = value.toDouble();
+ return { doubleVal - (int) doubleVal > 0.0 ? doubleVal : value.toLong() };
+ } else {
+ return {};
+ }
}
-}
-inline optional<Value> toValue(const mbgl::android::Value& value) {
- if (value.isNull()) {
- return {};
- } else if (value.isBool()) {
- return { value.toBool() };
- } else if (value.isString()) {
- return { value.toString() };
- } else if (value.isNumber()) {
- auto doubleVal = value.toDouble();
- return { doubleVal - (int) doubleVal > 0.0 ? doubleVal : value.toLong() };
- } else {
- return {};
+ static optional<GeoJSON> toGeoJSON(const mbgl::android::Value &value, Error &error) {
+ if (value.isNull() || !value.isString()) {
+ error = { "no json data found" };
+ return {};
+ }
+ return parseGeoJSON(value.toString(), error);
}
+};
+
+template <class T, class...Args>
+optional<T> convert(mbgl::android::Value&& value, Error& error, Args&&...args) {
+ return convert<T>(Convertible(std::move(value)), error, std::forward<Args>(args)...);
}
} // namespace conversion
diff --git a/platform/android/src/style/conversion/filter.hpp b/platform/android/src/style/conversion/filter.hpp
index 1f0abcf3a4..c154e88e7c 100644
--- a/platform/android/src/style/conversion/filter.hpp
+++ b/platform/android/src/style/conversion/filter.hpp
@@ -16,9 +16,8 @@ namespace conversion {
inline optional<mbgl::style::Filter> toFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) {
mbgl::optional<mbgl::style::Filter> filter;
if (jfilter) {
- Value filterValue(env, jfilter);
mbgl::style::conversion::Error error;
- auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(filterValue, error);
+ auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(Value(env, jfilter), error);
if (!converted) {
mbgl::Log::Error(mbgl::Event::JNI, "Error converting filter: " + error.message);
}
diff --git a/platform/android/src/style/conversion/geojson.hpp b/platform/android/src/style/conversion/geojson.hpp
deleted file mode 100644
index 748fe7361e..0000000000
--- a/platform/android/src/style/conversion/geojson.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include <mapbox/geojson.hpp>
-#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/conversion/geojson.hpp>
-#include <jni/jni.hpp>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const mbgl::android::Value& value, Error& error) const {
- if(value.isNull() || !value.isString()) {
- error = { "no json data found" };
- return {};
- }
-
- return convert<GeoJSON>(value.toString(), error);
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/platform/android/src/style/conversion/types.hpp b/platform/android/src/style/conversion/types.hpp
index a00f668c24..375d1a33aa 100644
--- a/platform/android/src/style/conversion/types.hpp
+++ b/platform/android/src/style/conversion/types.hpp
@@ -58,15 +58,15 @@ struct Converter<jni::jobject*, mbgl::style::IconTextFitType> {
};
template <>
-struct Converter<jni::jobject*, mbgl::style::TextJustifyType> {
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextJustifyType& value) const {
+struct Converter<jni::jobject*, mbgl::style::SymbolAnchorType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::SymbolAnchorType& value) const {
return convert<jni::jobject*, std::string>(env, toString(value));
}
};
template <>
-struct Converter<jni::jobject*, mbgl::style::TextAnchorType> {
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextAnchorType& value) const {
+struct Converter<jni::jobject*, mbgl::style::TextJustifyType> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextJustifyType& 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 e96de3b01e..a19ca33a2f 100644
--- a/platform/android/src/style/conversion/types_string_values.hpp
+++ b/platform/android/src/style/conversion/types_string_values.hpp
@@ -109,51 +109,34 @@ namespace conversion {
}
}
- // text-justify
- inline std::string toString(mbgl::style::TextJustifyType value) {
- switch (value) {
- case mbgl::style::TextJustifyType::Left:
- return "left";
- break;
- case mbgl::style::TextJustifyType::Center:
- return "center";
- break;
- case mbgl::style::TextJustifyType::Right:
- return "right";
- break;
- default:
- throw std::runtime_error("Not implemented");
- }
- }
-
- // text-anchor
- inline std::string toString(mbgl::style::TextAnchorType value) {
+ // icon-anchor
+ inline std::string toString(mbgl::style::SymbolAnchorType value) {
switch (value) {
- case mbgl::style::TextAnchorType::Center:
+ case mbgl::style::SymbolAnchorType::Center:
return "center";
break;
- case mbgl::style::TextAnchorType::Left:
+ case mbgl::style::SymbolAnchorType::Left:
return "left";
break;
- case mbgl::style::TextAnchorType::Right:
+ case mbgl::style::SymbolAnchorType::Right:
return "right";
break;
- case mbgl::style::TextAnchorType::Top:
+ case mbgl::style::SymbolAnchorType::Top:
return "top";
break;
- case mbgl::style::TextAnchorType::Bottom:
+ case mbgl::style::SymbolAnchorType::Bottom:
return "bottom";
break;
- case mbgl::style::TextAnchorType::TopLeft:
+ case mbgl::style::SymbolAnchorType::TopLeft:
return "top-left";
break;
- case mbgl::style::TextAnchorType::TopRight:
+ case mbgl::style::SymbolAnchorType::TopRight:
return "top-right";
break;
- case mbgl::style::TextAnchorType::BottomLeft:
+ case mbgl::style::SymbolAnchorType::BottomLeft:
return "bottom-left";
break;
- case mbgl::style::TextAnchorType::BottomRight:
+ case mbgl::style::SymbolAnchorType::BottomRight:
return "bottom-right";
break;
default:
@@ -161,6 +144,23 @@ namespace conversion {
}
}
+ // text-justify
+ inline std::string toString(mbgl::style::TextJustifyType value) {
+ switch (value) {
+ case mbgl::style::TextJustifyType::Left:
+ return "left";
+ break;
+ case mbgl::style::TextJustifyType::Center:
+ return "center";
+ break;
+ case mbgl::style::TextJustifyType::Right:
+ return "right";
+ break;
+ default:
+ throw std::runtime_error("Not implemented");
+ }
+ }
+
// text-transform
inline std::string toString(mbgl::style::TextTransformType value) {
switch (value) {
diff --git a/platform/android/src/style/conversion/url_or_tileset.hpp b/platform/android/src/style/conversion/url_or_tileset.hpp
index 00ef913d41..92c1182a63 100644
--- a/platform/android/src/style/conversion/url_or_tileset.hpp
+++ b/platform/android/src/style/conversion/url_or_tileset.hpp
@@ -17,18 +17,19 @@ namespace android {
// This conversion is expected not to fail because it's used only in contexts where
// the value was originally a String or TileSet object on the Java side. If it fails
// to convert, it's a bug in our serialization or Java-side static typing.
-inline variant<std::string, Tileset> convertURLOrTileset(const Value& value) {
+inline variant<std::string, Tileset> convertURLOrTileset(mbgl::android::Value&& value) {
using namespace mbgl::style::conversion;
- if (isObject(value)) {
+ const Convertible convertible(std::move(value));
+ if (isObject(convertible)) {
Error error;
- optional<Tileset> tileset = convert<Tileset>(value, error);
+ optional<Tileset> tileset = convert<Tileset>(convertible, error);
if (!tileset) {
throw std::logic_error(error.message);
}
return { *tileset };
} else {
- return { *toString(value) };
+ return { *toString(convertible) };
}
}
diff --git a/platform/android/src/style/layers/circle_layer.cpp b/platform/android/src/style/layers/circle_layer.cpp
index 96a9356679..4c7f69e956 100644
--- a/platform/android/src/style/layers/circle_layer.cpp
+++ b/platform/android/src/style/layers/circle_layer.cpp
@@ -142,6 +142,12 @@ namespace android {
return jni::Object<jni::ObjectTag>(*converted);
}
+ jni::Object<jni::ObjectTag> CircleLayer::getCirclePitchAlignment(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCirclePitchAlignment());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
jni::Object<jni::ObjectTag> CircleLayer::getCircleStrokeWidth(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleStrokeWidth());
@@ -236,6 +242,7 @@ namespace android {
METHOD(&CircleLayer::getCircleTranslate, "nativeGetCircleTranslate"),
METHOD(&CircleLayer::getCircleTranslateAnchor, "nativeGetCircleTranslateAnchor"),
METHOD(&CircleLayer::getCirclePitchScale, "nativeGetCirclePitchScale"),
+ METHOD(&CircleLayer::getCirclePitchAlignment, "nativeGetCirclePitchAlignment"),
METHOD(&CircleLayer::getCircleStrokeWidthTransition, "nativeGetCircleStrokeWidthTransition"),
METHOD(&CircleLayer::setCircleStrokeWidthTransition, "nativeSetCircleStrokeWidthTransition"),
METHOD(&CircleLayer::getCircleStrokeWidth, "nativeGetCircleStrokeWidth"),
diff --git a/platform/android/src/style/layers/circle_layer.hpp b/platform/android/src/style/layers/circle_layer.hpp
index 81737e8996..9d323e92bb 100644
--- a/platform/android/src/style/layers/circle_layer.hpp
+++ b/platform/android/src/style/layers/circle_layer.hpp
@@ -53,6 +53,8 @@ public:
jni::Object<jni::ObjectTag> getCirclePitchScale(jni::JNIEnv&);
+ jni::Object<jni::ObjectTag> getCirclePitchAlignment(jni::JNIEnv&);
+
jni::Object<jni::ObjectTag> getCircleStrokeWidth(jni::JNIEnv&);
void setCircleStrokeWidthTransition(jni::JNIEnv&, jlong duration, jlong delay);
jni::Object<TransitionOptions> getCircleStrokeWidthTransition(jni::JNIEnv&);
diff --git a/platform/android/src/style/layers/custom_layer.cpp b/platform/android/src/style/layers/custom_layer.cpp
index e6321a8efa..51a48520bf 100644
--- a/platform/android/src/style/layers/custom_layer.cpp
+++ b/platform/android/src/style/layers/custom_layer.cpp
@@ -7,11 +7,12 @@
namespace mbgl {
namespace android {
- CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong deinitializeFunction, jni::jlong context)
+ CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong contextLostFunction, jni::jlong deinitializeFunction, jni::jlong context)
: 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))
) {
@@ -52,7 +53,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>,
+ std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, 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 3e3f3bf77f..9e079c1288 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);
+ CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong);
CustomLayer(mbgl::Map&, mbgl::style::CustomLayer&);
diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp
index 02a1f0be82..31032b117f 100644
--- a/platform/android/src/style/layers/layer.cpp
+++ b/platform/android/src/style/layers/layer.cpp
@@ -4,11 +4,20 @@
#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/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/util/logging.hpp>
// Java -> C++ conversion
#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/filter.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/conversion/source.hpp>
@@ -78,10 +87,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 +96,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;
@@ -116,10 +121,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;
diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp
index 3a560a5deb..d44744a6cf 100644
--- a/platform/android/src/style/layers/symbol_layer.cpp
+++ b/platform/android/src/style/layers/symbol_layer.cpp
@@ -125,6 +125,18 @@ namespace android {
return jni::Object<jni::ObjectTag>(*converted);
}
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconAnchor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconAnchor());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconPitchAlignment(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconPitchAlignment());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
jni::Object<jni::ObjectTag> SymbolLayer::getTextPitchAlignment(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextPitchAlignment());
@@ -514,6 +526,8 @@ namespace android {
METHOD(&SymbolLayer::getIconPadding, "nativeGetIconPadding"),
METHOD(&SymbolLayer::getIconKeepUpright, "nativeGetIconKeepUpright"),
METHOD(&SymbolLayer::getIconOffset, "nativeGetIconOffset"),
+ METHOD(&SymbolLayer::getIconAnchor, "nativeGetIconAnchor"),
+ METHOD(&SymbolLayer::getIconPitchAlignment, "nativeGetIconPitchAlignment"),
METHOD(&SymbolLayer::getTextPitchAlignment, "nativeGetTextPitchAlignment"),
METHOD(&SymbolLayer::getTextRotationAlignment, "nativeGetTextRotationAlignment"),
METHOD(&SymbolLayer::getTextField, "nativeGetTextField"),
diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp
index 8366051c6e..417e5e143f 100644
--- a/platform/android/src/style/layers/symbol_layer.hpp
+++ b/platform/android/src/style/layers/symbol_layer.hpp
@@ -59,6 +59,10 @@ public:
jni::Object<jni::ObjectTag> getIconOffset(jni::JNIEnv&);
+ jni::Object<jni::ObjectTag> getIconAnchor(jni::JNIEnv&);
+
+ jni::Object<jni::ObjectTag> getIconPitchAlignment(jni::JNIEnv&);
+
jni::Object<jni::ObjectTag> getTextPitchAlignment(jni::JNIEnv&);
jni::Object<jni::ObjectTag> getTextRotationAlignment(jni::JNIEnv&);
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..a60b962e45
--- /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 GeoJsonOptions 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 780cc4b6f6..6d9ab9e22c 100644
--- a/platform/android/src/style/sources/geojson_source.cpp
+++ b/platform/android/src/style/sources/geojson_source.cpp
@@ -1,17 +1,19 @@
#include "geojson_source.hpp"
+#include <mbgl/renderer/query.hpp>
+
// 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>
@@ -27,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);
}
@@ -41,8 +43,10 @@ namespace android {
) {
}
- GeoJSONSource::GeoJSONSource(mbgl::Map& map, mbgl::style::GeoJSONSource& coreSource)
- : Source(map, coreSource) {
+ GeoJSONSource::GeoJSONSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
}
GeoJSONSource::~GeoJSONSource() = default;
@@ -52,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;
@@ -108,17 +112,17 @@ namespace android {
using namespace mbgl::android::geojson;
std::vector<mbgl::Feature> features;
- if (map) {
- features = map->querySourceFeatures(source.getID(), { {}, toFilter(env, jfilter) });
+ 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<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 938a20612c..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::Map&, 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 cc7e1e7404..0cd6995969 100644
--- a/platform/android/src/style/sources/image_source.cpp
+++ b/platform/android/src/style/sources/image_source.cpp
@@ -23,8 +23,10 @@ namespace android {
) {
}
- ImageSource::ImageSource(mbgl::Map& map, mbgl::style::ImageSource& coreSource)
- : Source(map, coreSource) {
+ ImageSource::ImageSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
}
ImageSource::~ImageSource() = default;
@@ -40,15 +42,14 @@ namespace android {
}
void ImageSource::setImage(jni::JNIEnv& env, jni::Object<Bitmap> bitmap) {
- UnassociatedImage image = util::unpremultiply(Bitmap::GetImage(env, bitmap));
- source.as<mbgl::style::ImageSource>()->setImage(std:: move(image));
+ source.as<mbgl::style::ImageSource>()->setImage(Bitmap::GetImage(env, bitmap));
}
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 309d17a299..f0af28d357 100644
--- a/platform/android/src/style/sources/image_source.hpp
+++ b/platform/android/src/style/sources/image_source.hpp
@@ -21,7 +21,7 @@ public:
ImageSource(jni::JNIEnv&, jni::String, jni::Object<LatLngQuad>);
- ImageSource(mbgl::Map&, mbgl::style::ImageSource&);
+ ImageSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
~ImageSource();
@@ -30,7 +30,8 @@ public:
void setImage(jni::JNIEnv&, jni::Object<Bitmap>);
- jni::jobject* createJavaPeer(jni::JNIEnv&);
+private:
+ jni::Object<Source> createJavaPeer(jni::JNIEnv&);
}; // class ImageSource
diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp
index 32fdb163b0..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::Map& map, mbgl::style::RasterSource& coreSource)
- : Source(map, 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 a79ccc10a4..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::Map&, 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 05f981953a..3b89b25d7d 100644
--- a/platform/android/src/style/sources/source.cpp
+++ b/platform/android/src/style/sources/source.cpp
@@ -15,29 +15,70 @@
#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"
+
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::Map& coreMap, mbgl::style::Source& coreSource) : source(coreSource) , map(&coreMap) {
+ 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() {
+ 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,22 +90,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);
- // Save pointer to the map
- this->map = &_map;
+ 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;
@@ -81,6 +148,13 @@ 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);
}
} // namespace android
diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp
index 49fc50d754..718f60b381 100644
--- a/platform/android/src/style/sources/source.hpp
+++ b/platform/android/src/style/sources/source.hpp
@@ -5,6 +5,7 @@
#include <mbgl/style/source.hpp>
#include "../value.hpp"
+#include "../../android_renderer_frontend.hpp"
#include <jni/jni.hpp>
@@ -20,45 +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::Map&, 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(JNIEnv&, jni::Object<Source>, mbgl::Map&, AndroidRendererFrontend&);
- void addToMap(mbgl::Map&);
-
- 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;
- // Map pointer is valid for newly created sources only after adding to the map
- mbgl::Map* map;
+ // 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 7ca6328e71..0000000000
--- a/platform/android/src/style/sources/sources.cpp
+++ /dev/null
@@ -1,53 +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 mbgl {
-namespace android {
-
-Source* initializeSourcePeer(mbgl::Map& map, mbgl::style::Source& coreSource) {
- Source* source;
- if (coreSource.is<mbgl::style::VectorSource>()) {
- source = new VectorSource(map, *coreSource.as<mbgl::style::VectorSource>());
- } else if (coreSource.is<mbgl::style::RasterSource>()) {
- source = new RasterSource(map, *coreSource.as<mbgl::style::RasterSource>());
- } else if (coreSource.is<mbgl::style::GeoJSONSource>()) {
- source = new GeoJSONSource(map, *coreSource.as<mbgl::style::GeoJSONSource>());
- } else if (coreSource.is<mbgl::style::ImageSource>()) {
- source = new ImageSource(map, *coreSource.as<mbgl::style::ImageSource>());
- } else {
- source = new UnknownSource(map, coreSource);
- }
-
- return source;
-}
-
-jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style::Source& coreSource) {
- std::unique_ptr<Source> peerSource = std::unique_ptr<Source>(initializeSourcePeer(map, coreSource));
- 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 09a8b35067..0000000000
--- a/platform/android/src/style/sources/sources.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include <mbgl/map/map.hpp>
-#include <mbgl/style/source.hpp>
-
-#include "source.hpp"
-
-#include <jni/jni.hpp>
-
-namespace mbgl {
-namespace android {
-
- mbgl::android::Source* initializeSourcePeer(mbgl::Map&, mbgl::style::Source&);
-
- jni::jobject* createJavaSourcePeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Source&);
-
- void registerNativeSources(jni::JNIEnv&);
-
-}
-}
diff --git a/platform/android/src/style/sources/unknown_source.cpp b/platform/android/src/style/sources/unknown_source.cpp
index 86736597c3..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::Map& map, mbgl::style::Source& coreSource)
- : Source(map, 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 3c37239792..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::Map&, 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 e2d9f60dec..9a9548d283 100644
--- a/platform/android/src/style/sources/vector_source.cpp
+++ b/platform/android/src/style/sources/vector_source.cpp
@@ -1,5 +1,7 @@
#include "vector_source.hpp"
+#include <mbgl/renderer/query.hpp>
+
// Java -> C++ conversion
#include "../android_conversion.hpp"
#include "../conversion/filter.hpp"
@@ -28,8 +30,10 @@ namespace android {
) {
}
- VectorSource::VectorSource(mbgl::Map& map, mbgl::style::VectorSource& coreSource)
- : Source(map, coreSource) {
+ VectorSource::VectorSource(jni::JNIEnv& env,
+ mbgl::style::Source& coreSource,
+ AndroidRendererFrontend& frontend)
+ : Source(env, coreSource, createJavaPeer(env), frontend) {
}
VectorSource::~VectorSource() = default;
@@ -46,17 +50,17 @@ namespace android {
using namespace mbgl::android::geojson;
std::vector<mbgl::Feature> features;
- if (map) {
- features = map->querySourceFeatures(source.getID(), { toVector(env, jSourceLayerIds), toFilter(env, jfilter) });
+ if (rendererFrontend) {
+ features = rendererFrontend->querySourceFeatures(source.getID(), { toVector(env, jSourceLayerIds), toFilter(env, jfilter) });
}
return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, features);
}
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 643b468338..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::Map&, 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/android/src/text/local_glyph_rasterizer.cpp b/platform/android/src/text/local_glyph_rasterizer.cpp
new file mode 100644
index 0000000000..ce1b0fc8fd
--- /dev/null
+++ b/platform/android/src/text/local_glyph_rasterizer.cpp
@@ -0,0 +1,126 @@
+#include <mbgl/text/local_glyph_rasterizer.hpp>
+#include <mbgl/util/i18n.hpp>
+#include <mbgl/util/platform.hpp>
+
+#include <jni/jni.hpp>
+
+#include "../attach_env.hpp"
+#include "../bitmap.hpp"
+
+#include "local_glyph_rasterizer_jni.hpp"
+
+/*
+ Android implementation of LocalGlyphRasterizer:
+ Draws CJK glyphs using locally available fonts.
+
+ Follows pattern of GL JS implementation in that:
+ - Only CJK glyphs are drawn locally (because we can guess their metrics effectively)
+ * Render size/metrics determined experimentally using Noto Sans
+ - Configuration is done at map creation time by setting a "font family"
+ * JS uses a CSS font-family, this uses android.graphics.Typeface
+ https://developer.android.com/reference/android/graphics/Typeface.html
+ - We use heuristics to extract a font-weight based on the incoming font stack
+ * JS tries to extract multiple weights, this implementation only looks for
+ "bold"
+
+ mbgl::LocalGlyphRasterizer is the portable interface
+ mbgl::LocalGlyphRasterizer::Impl stores platform-specific configuration data
+ mbgl::android::LocalGlyphRasterizer is the JNI wrapper
+ com.mapbox.mapboxsdk.text.LocalGlyphRasterizer is the Java implementation that
+ actually does the drawing
+ */
+
+namespace mbgl {
+namespace android {
+
+PremultipliedImage LocalGlyphRasterizer::drawGlyphBitmap(const std::string& fontFamily, const bool bold, const GlyphID glyphID) {
+ UniqueEnv env { AttachEnv() };
+
+ using Signature = jni::Object<Bitmap>(jni::String, jni::jboolean, jni::jchar);
+ auto method = javaClass.GetStaticMethod<Signature>(*env, "drawGlyphBitmap");
+
+ jni::String jniFontFamily = jni::Make<jni::String>(*env, fontFamily);
+
+ auto javaBitmap = javaClass.Call(*env,
+ method,
+ jniFontFamily,
+ static_cast<jni::jboolean>(bold),
+ static_cast<jni::jchar>(glyphID));
+
+ PremultipliedImage result = Bitmap::GetImage(*env, javaBitmap);
+ jni::DeleteLocalRef(*env, javaBitmap);
+ return result;
+}
+
+void LocalGlyphRasterizer::registerNative(jni::JNIEnv& env) {
+ javaClass = *jni::Class<LocalGlyphRasterizer>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<LocalGlyphRasterizer> LocalGlyphRasterizer::javaClass;
+
+} // namespace android
+
+class LocalGlyphRasterizer::Impl {
+public:
+ Impl(const optional<std::string> fontFamily_)
+ : fontFamily(fontFamily_)
+ {}
+
+ bool isConfigured() const {
+ return bool(fontFamily);
+ }
+
+ PremultipliedImage drawGlyphBitmap(const FontStack& fontStack, GlyphID glyphID) {
+ bool bold = false;
+ for (auto font : fontStack) {
+ std::string lowercaseFont = platform::lowercase(font);
+ if (lowercaseFont.find("bold") != std::string::npos) {
+ bold = true;
+ }
+ }
+ return android::LocalGlyphRasterizer::drawGlyphBitmap(*fontFamily, bold, glyphID);
+ }
+
+private:
+ optional<std::string> fontFamily;
+};
+
+LocalGlyphRasterizer::LocalGlyphRasterizer(const optional<std::string> fontFamily)
+ : impl(std::make_unique<Impl>(fontFamily))
+{}
+
+LocalGlyphRasterizer::~LocalGlyphRasterizer()
+{}
+
+bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID glyphID) {
+ return util::i18n::allowsFixedWidthGlyphGeneration(glyphID) && impl->isConfigured();
+}
+
+Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack& fontStack, GlyphID glyphID) {
+ Glyph fixedMetrics;
+ if (!impl->isConfigured()) {
+ return fixedMetrics;
+ }
+
+ fixedMetrics.id = glyphID;
+
+ Size size(35, 35);
+
+ fixedMetrics.metrics.width = size.width;
+ fixedMetrics.metrics.height = size.height;
+ fixedMetrics.metrics.left = 3;
+ fixedMetrics.metrics.top = -10;
+ fixedMetrics.metrics.advance = 24;
+
+ PremultipliedImage rgbaBitmap = impl->drawGlyphBitmap(fontStack, glyphID);
+
+ // Copy alpha values from RGBA bitmap into the AlphaImage output
+ fixedMetrics.bitmap = AlphaImage(size);
+ for (uint32_t i = 0; i < size.width * size.height; i++) {
+ fixedMetrics.bitmap.data[i] = rgbaBitmap.data[4 * i + 3];
+ }
+
+ return fixedMetrics;
+}
+
+} // namespace mbgl
diff --git a/platform/android/src/text/local_glyph_rasterizer_jni.hpp b/platform/android/src/text/local_glyph_rasterizer_jni.hpp
new file mode 100644
index 0000000000..38d98d5368
--- /dev/null
+++ b/platform/android/src/text/local_glyph_rasterizer_jni.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+
+#include <jni/jni.hpp>
+
+/*
+ android::LocalGlyphRasterizer is the JNI wrapper of
+ com/mapbox/mapboxsdk/text/LocalGlyphRasterizer
+
+ mbgl::LocalGlyphRasterizer is the portable interface
+ Both implementations are in local_glyph_rasterizer.cpp
+ */
+
+namespace mbgl {
+namespace android {
+
+class LocalGlyphRasterizer {
+public:
+ static PremultipliedImage drawGlyphBitmap(const std::string& fontFamily, const bool bold, const char16_t glyphID);
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/text/LocalGlyphRasterizer"; };
+
+ static jni::Class<LocalGlyphRasterizer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/tests/docs/UI_TESTS.md b/platform/android/tests/docs/UI_TESTS.md
index 6d8541a406..1014b56845 100644
--- a/platform/android/tests/docs/UI_TESTS.md
+++ b/platform/android/tests/docs/UI_TESTS.md
@@ -61,7 +61,7 @@ You can generate JaCoCo reports from espresso tests by
## Running Espresso test automatically on AWS Device Farm
To run tests on AWS device farm you need to execute `./gradlew -Pmapbox.abis=none devicefarmUpload`.
You can configure the different steps in the testapp `build.gradle`.
-AWS credentials are found in bitrise.
+AWS credentials are found in CircleCI.
diff --git a/platform/android/tests/docs/UNIT_TESTS.md b/platform/android/tests/docs/UNIT_TESTS.md
index fefb435684..458e8869f3 100644
--- a/platform/android/tests/docs/UNIT_TESTS.md
+++ b/platform/android/tests/docs/UNIT_TESTS.md
@@ -77,7 +77,7 @@ The Unit tests are executed as part of the build process on our CI and are
automatically run for each new commit pushed to this repo. If a Unit tests
fails, this will fail and stop the build.
-You can find this gradle command in our [buildscript](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/bitrise.yml#L48):
+You can find this gradle command in our [buildscript](https://github.com/mapbox/mapbox-gl-native/blob/master/circle.yml#L146-L215):
```
$ ./gradlew -Pmapbox.abis=none testReleaseUnitTest
diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs
index 153639371c..06a8907704 100644
--- a/platform/darwin/docs/guides/For Style Authors.md.ejs
+++ b/platform/darwin/docs/guides/For Style Authors.md.ejs
@@ -121,6 +121,8 @@ for ink economy before printing the map view.
<% if (iOS) { -%>
For more information about user interface design, consult Apple’s
[_iOS Human Interface Guidelines_](https://developer.apple.com/ios/human-interface-guidelines/).
+To learn more about designing maps for mobile devices, see [Nathaniel Slaughter's blog post](https://www.mapbox.com/blog/designing-maps-for-mobile-devices/) on
+the subject.
<% } else { -%>
For more information about user interface design, consult Apple’s
[_macOS Human Interface Guidelines_](https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/).
diff --git a/platform/darwin/docs/guides/Working with GeoJSON Data.md b/platform/darwin/docs/guides/Working with GeoJSON Data.md
index 57aaa3855d..f3b3dc0918 100644
--- a/platform/darwin/docs/guides/Working with GeoJSON Data.md
+++ b/platform/darwin/docs/guides/Working with GeoJSON Data.md
@@ -81,8 +81,10 @@ Linear ring | `MGLPolygon.coordinates`, `MGLPolygon.interiorPolygons`
A `Feature` object in GeoJSON corresponds to an instance of an `MGLShape`
subclass conforming to the `MGLFeature` protocol. There is a distinct
`MGLFeature`-conforming class for each type of geometry that a GeoJSON feature
-can contain. This allows features to be used as shapes where convenient. For
-example, some features can be added to a map view as annotations.
+can contain. This allows features to be used as raw shapes where convenient. For
+example, some features can be added to a map view as annotations. Note that
+identifiers and attributes will not be available for feature querying when a
+feature is used as an annotation.
In contrast to the GeoJSON standard, it is possible for `MGLShape` subclasses
other than `MGLPointAnnotation` to straddle the antimeridian.
diff --git a/platform/darwin/docs/theme/assets/css/jazzy.css.scss b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
index ad0a3b7082..103ba601dc 100644
--- a/platform/darwin/docs/theme/assets/css/jazzy.css.scss
+++ b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
@@ -386,6 +386,7 @@ pre code {
.nav-group-task[data-name="MGLStyleFunction"],
.nav-group-task[data-name="MGLStyleLayer"],
.nav-group-task[data-name="MGLTileSource"],
+.nav-group-task[data-name="MGLAbstractShapeSource"],
.nav-group-task[data-name="MGLVectorStyleLayer"] {
.nav-group-task-link::after {
@extend %nav-group-task-gloss;
diff --git a/platform/darwin/mbgl/gl/gl_impl.hpp b/platform/darwin/mbgl/gl/gl_impl.hpp
new file mode 100644
index 0000000000..b4c062a474
--- /dev/null
+++ b/platform/darwin/mbgl/gl/gl_impl.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "TargetConditionals.h"
+#if TARGET_OS_IPHONE
+ #include <OpenGLES/ES2/gl.h>
+ #include <OpenGLES/ES2/glext.h>
+#elif TARGET_IPHONE_SIMULATOR
+ #include <OpenGLES/ES2/gl.h>
+ #include <OpenGLES/ES2/glext.h>
+#elif TARGET_OS_MAC
+ #include <OpenGL/OpenGL.h>
+ #include <OpenGL/gl.h>
+ #include <OpenGL/glext.h>
+#else
+ #error Unsupported Apple platform
+#endif
diff --git a/platform/darwin/mbgl/util/image+MGLAdditions.hpp b/platform/darwin/mbgl/util/image+MGLAdditions.hpp
index c738b4523d..c5343af4de 100644
--- a/platform/darwin/mbgl/util/image+MGLAdditions.hpp
+++ b/platform/darwin/mbgl/util/image+MGLAdditions.hpp
@@ -5,7 +5,7 @@
#include <CoreGraphics/CGImage.h>
// Creates a CGImage from a PremultipliedImage, taking over the memory ownership.
-CGImageRef CGImageFromMGLPremultipliedImage(mbgl::PremultipliedImage&&);
+CGImageRef CGImageCreateWithMGLPremultipliedImage(mbgl::PremultipliedImage&&);
// Creates a PremultipliedImage by copying the pixels of the CGImage.
// Does not alter the retain count of the supplied CGImage.
diff --git a/platform/darwin/resources/bg.lproj/Foundation.strings b/platform/darwin/resources/bg.lproj/Foundation.strings
new file mode 100644
index 0000000000..f2a9c6eae0
--- /dev/null
+++ b/platform/darwin/resources/bg.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* 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, short */
+"COMPASS_EbN_SHORT" = "EbN";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "ийст-тен-саут";
+
+/* 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, short */
+"COMPASS_NbE_SHORT" = "NbE";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "норд-тен-уест";
+
+/* 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, short */
+"COMPASS_NEbE_SHORT" = "NEbE";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "нордийст-тен-норд";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "NEnN";
+
+/* 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, short */
+"COMPASS_NWbN_SHORT" = "NWbN";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "нордуест-тен-уест";
+
+/* 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, short */
+"COMPASS_SbE_SHORT" = "SbE";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "саут-тен-уест";
+
+/* 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, short */
+"COMPASS_SEbE_SHORT" = "SEbE";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "саутийст-тен-саут";
+
+/* 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, short */
+"COMPASS_SWbS_SHORT" = "SWbS";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "саутуест-тен-уест";
+
+/* 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, short */
+"COMPASS_WbN_SHORT" = "WbN";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "уест-тен-саут";
+
+/* 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" = "%@З";
+
diff --git a/platform/darwin/resources/bg.lproj/Foundation.stringsdict b/platform/darwin/resources/bg.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..fcaf0a48c1
--- /dev/null
+++ b/platform/darwin/resources/bg.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 градус</string>
+ <key>other</key>
+ <string>%d градуса</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 минута</string>
+ <key>other</key>
+ <string>%d минути</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 секунда</string>
+ <key>other</key>
+ <string>%d секунди</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/darwin/resources/ca.lproj/Foundation.strings b/platform/darwin/resources/ca.lproj/Foundation.strings
index e36ea9ed8a..f86442f26f 100644
--- a/platform/darwin/resources/ca.lproj/Foundation.strings
+++ b/platform/darwin/resources/ca.lproj/Foundation.strings
@@ -1,8 +1,8 @@
/* Clock position format, long: {hours} o’clock */
-"CLOCK_FMT_LONG" = "%@ en punt";
+"CLOCK_FMT_LONG" = "a les %@";
/* Clock position format, medium: {hours} o’clock */
-"CLOCK_FMT_MEDIUM" = "%@ en punt";
+"CLOCK_FMT_MEDIUM" = "a les %@";
/* Clock position format, short: {hours}:00 */
"CLOCK_FMT_SHORT" = "%@:00";
diff --git a/platform/darwin/resources/de.lproj/Foundation.stringsdict b/platform/darwin/resources/de.lproj/Foundation.stringsdict
index 776528a99c..fb9073fa96 100644
--- a/platform/darwin/resources/de.lproj/Foundation.stringsdict
+++ b/platform/darwin/resources/de.lproj/Foundation.stringsdict
@@ -2,6 +2,22 @@
<!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 Grad</string>
+ </dict>
+ </dict>
<key>COORD_MIN_LONG</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
diff --git a/platform/darwin/resources/fr.lproj/Foundation.strings b/platform/darwin/resources/fr.lproj/Foundation.strings
new file mode 100644
index 0000000000..d2f6c1f6df
--- /dev/null
+++ b/platform/darwin/resources/fr.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "%@ heures";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "%@ heures";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@h 00";
+
+/* East, long */
+"COMPASS_E_LONG" = "est";
+
+/* East, short */
+"COMPASS_E_SHORT" = "E";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "est par nord";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "EpN";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "est par sud";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "EpS";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "est-nord-est";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "ENE";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "est-sud-est";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "ESE";
+
+/* North, long */
+"COMPASS_N_LONG" = "nord";
+
+/* North, short */
+"COMPASS_N_SHORT" = "N";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "nord par est";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "NpE";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "nord par ouest";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "NpO";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "nord-est";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "NE";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "nord-est par est";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "NEpE";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "nord-est par nord";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "NEpN";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "nord-nord-est";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "NNE";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "nord-nord-ouest";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "NNO";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "nord-ouest";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "NO";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "nord-ouest par nord";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "NOpN";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "nord-ouest par ouest";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "NOpO";
+
+/* South, long */
+"COMPASS_S_LONG" = "sud";
+
+/* South, short */
+"COMPASS_S_SHORT" = "S";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "sud par est";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "SpE";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "sud par ouest";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "SpO";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "sud-est";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "SE";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "sud-est par est";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "SEpE";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "sud-est par sud";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "SEpS";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "sud-sud-est";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "SSE";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "sud-sud-ouest";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "SSO";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "sud-ouest";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "SO";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "sud-ouest par sud";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "SOpS";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "sud-ouest par ouest";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "SOpO";
+
+/* West, long */
+"COMPASS_W_LONG" = "ouest";
+
+/* West, short */
+"COMPASS_W_SHORT" = "O";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "ouest par nord";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "OpN";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "ouest par sud";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "OpS";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "ouest-nord-ouest";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "ONO";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "ouest-sud-ouest";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "OSO";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d degré(s)";
+
+/* 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$@ et %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$@ et %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" = "%@ est";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@ est";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@E";
+
+/* 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 minute(s)";
+
+/* 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" = "%@ sud";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@ sud";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@S";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d seconde(s)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@ ouest";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@ ouest";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@O";
+
diff --git a/platform/darwin/resources/hu.lproj/Foundation.stringsdict b/platform/darwin/resources/hu.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..a594100046
--- /dev/null
+++ b/platform/darwin/resources/hu.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 fok</string>
+ <key>other</key>
+ <string>%d fok</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 perc</string>
+ <key>other</key>
+ <string>%d perc</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 másodperc</string>
+ <key>other</key>
+ <string>%d másodperc</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/darwin/resources/lt.lproj/Foundation.stringsdict b/platform/darwin/resources/lt.lproj/Foundation.stringsdict
index e3bb31838a..4ab9d4a8bf 100644
--- a/platform/darwin/resources/lt.lproj/Foundation.stringsdict
+++ b/platform/darwin/resources/lt.lproj/Foundation.stringsdict
@@ -16,8 +16,6 @@
<string>%d laipsniai</string>
<key>few</key>
<string>%d laipsniai</string>
- <key>many</key>
- <string>%d laipsniai</string>
<key>other</key>
<string>%d laipsnių</string>
</dict>
@@ -36,8 +34,6 @@
<string>%d minutė</string>
<key>few</key>
<string>%d minutės</string>
- <key>many</key>
- <string>%d minutės</string>
<key>other</key>
<string>%d minučių</string>
</dict>
@@ -56,8 +52,6 @@
<string>%d sekundė</string>
<key>few</key>
<string>%d sekundės</string>
- <key>many</key>
- <string>%d sekundės</string>
<key>other</key>
<string>%d sekundžių</string>
</dict>
diff --git a/platform/darwin/resources/pt-BR.lproj/Foundation.strings b/platform/darwin/resources/pt-BR.lproj/Foundation.strings
new file mode 100644
index 0000000000..71a7f7e4a5
--- /dev/null
+++ b/platform/darwin/resources/pt-BR.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "São %@ horas";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "São %@ horas";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@:00";
+
+/* East, long */
+"COMPASS_E_LONG" = "leste";
+
+/* East, short */
+"COMPASS_E_SHORT" = "L";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "lest pelo norte";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "LpN";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "leste pelo sul";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "LpS";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "lés-nordeste";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "ENE";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "lés-sudeste";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "ESE";
+
+/* North, long */
+"COMPASS_N_LONG" = "norte";
+
+/* North, short */
+"COMPASS_N_SHORT" = "N";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "norte pelo leste";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "NpL";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "norte pelo oeste";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "NpO";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "nordeste";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "NE";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "nordeste pelo leste";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "NEpL";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "nordeste pelo norte";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "NEpN";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "nor-nordeste";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "NNE";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "nor-noroeste";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "NNO";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "noroeste";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "NO";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "noroeste pelo norte";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "NOpN";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "noroeste pelo oeste";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "NOpO";
+
+/* South, long */
+"COMPASS_S_LONG" = "sul";
+
+/* South, short */
+"COMPASS_S_SHORT" = "S";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "sul pelo leste";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "SpL";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "sul pelo oeste";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "SbO";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "sudeste";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "SE";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "sudeste pelo leste";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "SEpL";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "sudeste pelo sul";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "SEpS";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "sul-sudeste";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "SSE";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "su-sudoeste";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "SSO";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "sudoeste";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "SO";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "sudoeste pelo sul";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "SOpS";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "sudoeste pelo oeste";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "SOpO";
+
+/* West, long */
+"COMPASS_W_LONG" = "oeste";
+
+/* West, short */
+"COMPASS_W_SHORT" = "O";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "oeste pelo norte";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "OpN";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "oeste pelo sul";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "OpS";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "oés-noroeste";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "ONO";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "oés-sudoeste";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "OSO";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d grau(s)";
+
+/* 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$@ e %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$@, e %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" = "%@ leste";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@ leste";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@L";
+
+/* Coordinate pair format, long: {latitude}, {longitude} */
+"COORD_FMT_LONG" = "%1$@ por %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 minuto(s)";
+
+/* Minutes format, medium: {minutes} */
+"COORD_MIN_MEDIUM" = "%d′";
+
+/* Minutes format, short: {minutes} */
+"COORD_MIN_SHORT" = "%d′";
+
+/* North latitude format, long: {latitude} */
+"COORD_N_LONG" = "%@ norte";
+
+/* North latitude format, medium: {latitude} */
+"COORD_N_MEDIUM" = "%@ norte";
+
+/* North latitude format, short: {latitude} */
+"COORD_N_SHORT" = "%@N";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@ sul";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@ sul";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@S";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d segundo(s)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@ oeste";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@ oeste";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@O";
+
diff --git a/platform/darwin/resources/ru.lproj/Foundation.strings b/platform/darwin/resources/ru.lproj/Foundation.strings
new file mode 100644
index 0000000000..fd9eb04ada
--- /dev/null
+++ b/platform/darwin/resources/ru.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* 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, short */
+"COMPASS_EbN_SHORT" = "СВ";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "юго-восток";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "ЮВ";
+
+/* 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, short */
+"COMPASS_NbE_SHORT" = "СВ";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "северо-запад";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "СЗ";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "северо-восток";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "СВ";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "восток-северо-восток";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "ВСВ";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "северо-северо-восток";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "ССВ";
+
+/* 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, short */
+"COMPASS_NWbN_SHORT" = "ССЗ";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "запад-северо-запад";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "ЗСЗ";
+
+/* South, long */
+"COMPASS_S_LONG" = "юг";
+
+/* South, short */
+"COMPASS_S_SHORT" = "Ю";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "юго-восток";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "ЮВ";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "юго-запад";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "ЮЗ";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "юго-восток";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "ЮВ";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "восток-юго-восток";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "ВЮВ";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "юго-юго-восток";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "ЮЮВ";
+
+/* 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, short */
+"COMPASS_SWbS_SHORT" = "ЮЮЗ";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "восток-юго-восток";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "ВЮВ";
+
+/* West, long */
+"COMPASS_W_LONG" = "запад";
+
+/* West, short */
+"COMPASS_W_SHORT" = "З";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "северо-запад";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "СЗ";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "юго-запад";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "ЮГ";
+
+/* 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" = "%@E";
+
+/* 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" = "%@N";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@южной широты";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@южной широты";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@S";
+
+/* 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" = "%@W";
+
diff --git a/platform/darwin/resources/uk.lproj/Foundation.strings b/platform/darwin/resources/uk.lproj/Foundation.strings
new file mode 100644
index 0000000000..32a006829f
--- /dev/null
+++ b/platform/darwin/resources/uk.lproj/Foundation.strings
@@ -0,0 +1,291 @@
+/* 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, short */
+"COMPASS_EbN_SHORT" = "ПнСх";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "південний схід";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "ПдСх";
+
+/* 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, short */
+"COMPASS_NbE_SHORT" = "ПнСх";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "північний захід";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "ПнЗх";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "північний схід";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "ПнСх";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "північний схід - схід";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "ПнСх-Сх";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "північний счхід - північ";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "ПнСх-Пн";
+
+/* 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, short */
+"COMPASS_NWbN_SHORT" = "ПнЗх-Пн";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "північний захід - захід";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "ПнЗх-Зх";
+
+/* South, long */
+"COMPASS_S_LONG" = "південь";
+
+/* South, short */
+"COMPASS_S_SHORT" = "Пд";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "південний схід";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "ПдСх";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "південний захід";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "ПдЗх";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "південний схід";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "ПдСх";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "південний схід - схід";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "ПдСх-Сх";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "південний схід - південь";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "ПдСх-Пд";
+
+/* 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, short */
+"COMPASS_SWbS_SHORT" = "ПдЗх-Пд";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "південний захід - захід";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "ПдЗх-Зх";
+
+/* West, long */
+"COMPASS_W_LONG" = "захід";
+
+/* West, short */
+"COMPASS_W_SHORT" = "Зх";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "північний захід";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "ПнЗх";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "південний захід";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "ПдЗх";
+
+/* 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" = "%@Зх";
+
diff --git a/platform/darwin/resources/uk.lproj/Foundation.stringsdict b/platform/darwin/resources/uk.lproj/Foundation.stringsdict
index f35f0516e1..fdad193d4d 100644
--- a/platform/darwin/resources/uk.lproj/Foundation.stringsdict
+++ b/platform/darwin/resources/uk.lproj/Foundation.stringsdict
@@ -16,10 +16,8 @@
<string>%d градус</string>
<key>few</key>
<string>%d градуси</string>
- <key>many</key>
- <string>%d градусів</string>
<key>other</key>
- <string>%d градуса</string>
+ <string>%d градусів</string>
</dict>
</dict>
<key>COORD_MIN_LONG</key>
@@ -33,13 +31,11 @@
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
- <string>%d мінута</string>
+ <string>%d хвилина</string>
<key>few</key>
- <string>%d мінути</string>
- <key>many</key>
- <string>%d мінут</string>
+ <string>%d хвилини</string>
<key>other</key>
- <string>%d мінути</string>
+ <string>%d хвилин</string>
</dict>
</dict>
<key>COORD_SEC_LONG</key>
@@ -56,10 +52,8 @@
<string>%d секунда</string>
<key>few</key>
<string>%d секунди</string>
- <key>many</key>
- <string>%d секунд</string>
<key>other</key>
- <string>%d секунди</string>
+ <string>%d секунд</string>
</dict>
</dict>
</dict>
diff --git a/platform/darwin/resources/vi.lproj/Foundation.stringsdict b/platform/darwin/resources/vi.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..d85707f7ff
--- /dev/null
+++ b/platform/darwin/resources/vi.lproj/Foundation.stringsdict
@@ -0,0 +1,48 @@
+<?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>other</key>
+ <string>%d độ</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>other</key>
+ <string>%d phút</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>other</key>
+ <string>%d giây</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/darwin/resources/zh-Hant.lproj/Foundation.strings b/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
index 41b1d2baae..8a02bcfac0 100644
--- a/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
+++ b/platform/darwin/resources/zh-Hant.lproj/Foundation.strings
@@ -1,4 +1,4 @@
-/* Clock position format, long: {hours} o’clock */
+/* Clock position format, long: {hours} o’clock */
"CLOCK_FMT_LONG" = "%@點";
/* Clock position format, medium: {hours} o’clock */
@@ -26,16 +26,16 @@
"COMPASS_EbS_SHORT" = "東微南";
/* East-northeast, long */
-"COMPASS_ENE_LONG" = "東北偏東";
+"COMPASS_ENE_LONG" = "東北東";
/* East-northeast, short */
-"COMPASS_ENE_SHORT" = "東北偏東";
+"COMPASS_ENE_SHORT" = "東北東";
/* East-southeast, long */
-"COMPASS_ESE_LONG" = "東南偏東";
+"COMPASS_ESE_LONG" = "東南東";
/* East-southeast, short */
-"COMPASS_ESE_SHORT" = "東南偏東";
+"COMPASS_ESE_SHORT" = "東南東";
/* North, long */
"COMPASS_N_LONG" = "北";
@@ -74,16 +74,16 @@
"COMPASS_NEbN_SHORT" = "東北微北";
/* North-northeast, long */
-"COMPASS_NNE_LONG" = "東北偏北";
+"COMPASS_NNE_LONG" = "北北東";
/* North-northeast, short */
-"COMPASS_NNE_SHORT" = "東北偏北";
+"COMPASS_NNE_SHORT" = "北北東";
/* North-northwest, long */
-"COMPASS_NNW_LONG" = "西北偏北";
+"COMPASS_NNW_LONG" = "北北西";
/* North-northwest, short */
-"COMPASS_NNW_SHORT" = "西北偏北";
+"COMPASS_NNW_SHORT" = "北北西";
/* Northwest, long */
"COMPASS_NW_LONG" = "西北";
@@ -140,16 +140,16 @@
"COMPASS_SEbS_SHORT" = "東南微南";
/* South-southeast, long */
-"COMPASS_SSE_LONG" = "東南偏南";
+"COMPASS_SSE_LONG" = "南南東";
/* South-southeast, short */
-"COMPASS_SSE_SHORT" = "東南偏南";
+"COMPASS_SSE_SHORT" = "南南東";
/* South-southwest, long */
-"COMPASS_SSW_LONG" = "西南偏南";
+"COMPASS_SSW_LONG" = "南南西";
/* South-southwest, short */
-"COMPASS_SSW_SHORT" = "西南偏南";
+"COMPASS_SSW_SHORT" = "南南西";
/* Southwest, long */
"COMPASS_SW_LONG" = "西南";
@@ -158,16 +158,16 @@
"COMPASS_SW_SHORT" = "西南";
/* Southwest by south, long */
-"COMPASS_SWbS_LONG" = "西南偏南";
+"COMPASS_SWbS_LONG" = "西南微南";
/* Southwest by south, short */
-"COMPASS_SWbS_SHORT" = "西南偏南";
+"COMPASS_SWbS_SHORT" = "西南微南";
/* Southwest by west, long */
-"COMPASS_SWbW_LONG" = "西南偏西";
+"COMPASS_SWbW_LONG" = "西南微西";
/* Southwest by west, short */
-"COMPASS_SWbW_SHORT" = "西南偏西";
+"COMPASS_SWbW_SHORT" = "西南微西";
/* West, long */
"COMPASS_W_LONG" = "西";
@@ -188,16 +188,16 @@
"COMPASS_WbS_SHORT" = "西微南";
/* West-northwest, long */
-"COMPASS_WNW_LONG" = "西北偏西";
+"COMPASS_WNW_LONG" = "西北西";
/* West-northwest, short */
-"COMPASS_WNW_SHORT" = "西北偏西";
+"COMPASS_WNW_SHORT" = "西北西";
/* West-southwest, long */
-"COMPASS_WSW_LONG" = "西南偏西";
+"COMPASS_WSW_LONG" = "西南西";
/* West-southwest, short */
-"COMPASS_WSW_SHORT" = "西南偏西";
+"COMPASS_WSW_SHORT" = "西南西";
/* Degrees format, long */
"COORD_DEG_LONG" = "%d度";
diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js
index 7efc8d441c..4ee86e65da 100644
--- a/platform/darwin/scripts/generate-style-code.js
+++ b/platform/darwin/scripts/generate-style-code.js
@@ -148,7 +148,13 @@ global.mbglTestValue = function (property, layerType) {
if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) {
type = 'Alignment';
}
+ if (/^(text|icon)-anchor$/.test(originalPropertyName(property))) {
+ type = 'SymbolAnchor'
+ }
let value = camelize(_.last(_.keys(property.values)));
+ if (property['light-property']) {
+ return `mbgl::style::Light${type}Type::${value}`;
+ }
return `mbgl::style::${type}Type::${value}`;
}
case 'color':
@@ -225,6 +231,9 @@ global.propertyDoc = function (propertyName, property, layerType, kind) {
let doc = property.doc.replace(/`([^`]+?)` is set to `([^`]+?)`/g, function (m, peerPropertyName, propertyValue, offset, str) {
let otherProperty = camelizeWithLeadingLowercase(peerPropertyName);
let otherValue = objCType(layerType, peerPropertyName) + camelize(propertyValue);
+ if (property.type == 'array' && kind == 'light') {
+ otherValue = propertyValue;
+ }
return '`' + `${otherProperty}` + '` is set to `' + `${otherValue}` + '`';
});
// Match references to our own property values.
@@ -256,7 +265,7 @@ global.propertyDoc = function (propertyName, property, layerType, kind) {
if (kind !== 'enum') {
if ('default' in property) {
doc += `\n\nThe default value of this property is ${propertyDefault(property, layerType)}.`;
- if (!property.required) {
+ if (!property.required && kind != 'light') {
doc += ' Set this property to `nil` to reset it to the default value.';
}
}
@@ -348,6 +357,8 @@ global.describeValue = function (value, property, layerType) {
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)}\``;
} else {
let objCType = global.objCType(layerType, property.name);
displayValue = `\`${objCType}${camelize(value)}\``;
@@ -382,6 +393,8 @@ global.describeValue = function (value, property, layerType) {
case 'offset':
case 'translate':
return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${value[0]}${units} rightward and ${value[1]}${units} downward`;
+ case 'position':
+ return 'an `MGLSphericalPosition` struct set to' + ` ${value[0]} radial, ${value[1]} azimuthal and ${value[2]} polar`;
default:
return 'the array `' + value.join('`, `') + '`';
}
@@ -418,6 +431,7 @@ global.propertyType = function (property) {
return 'NSArray<NSString *> *';
case 'padding':
return 'NSValue *';
+ case 'position':
case 'offset':
case 'translate':
return 'NSValue *';
@@ -457,6 +471,8 @@ global.valueTransformerArguments = function (property) {
return ['std::vector<std::string>', objCType, 'std::string'];
case 'padding':
return ['std::array<float, 4>', objCType];
+ case 'position':
+ return ['mbgl::style::Position', objCType];
case 'offset':
case 'translate':
return ['std::array<float, 2>', objCType];
@@ -478,12 +494,18 @@ global.mbglType = function(property) {
return 'std::string';
case 'enum': {
let type = camelize(originalPropertyName(property));
+ if (property['light-property']) {
+ return `mbgl::style::Light${type}Type`;
+ }
if (/-translate-anchor$/.test(originalPropertyName(property))) {
type = 'TranslateAnchor';
}
if (/-(rotation|pitch)-alignment$/.test(originalPropertyName(property))) {
type = 'Alignment';
}
+ if (/^(text|icon)-anchor$/.test(originalPropertyName(property))) {
+ type = 'SymbolAnchor'
+ }
return `mbgl::style::${type}Type`;
}
case 'color':
@@ -499,6 +521,8 @@ global.mbglType = function(property) {
case 'offset':
case 'translate':
return 'std::array<float, 2>';
+ case 'position':
+ return 'mbgl::style::Position';
default:
throw new Error(`unknown array type for ${property.name}`);
}
@@ -519,6 +543,17 @@ global.setSourceLayer = function() {
return `_layer->setSourceLayer(sourceLayer.UTF8String);`
};
+const lightProperties = Object.keys(spec['light']).reduce((memo, name) => {
+ var property = spec['light'][name];
+ property.name = name;
+ property['light-property'] = true;
+ memo.push(property);
+ return memo;
+}, []);
+
+const lightDoc = spec['light-cocoa-doc'];
+const lightType = 'light';
+
const layerH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.h.ejs', 'utf8'), { strict: true });
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});
@@ -526,6 +561,14 @@ const forStyleAuthorsMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guid
const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Using Style Functions at Runtime.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 }));
+
+
const layers = _(spec.layer.type.values).map((value, layerType) => {
const layoutProperties = Object.keys(spec[`layout_${layerType}`]).reduce((memo, name) => {
if (name !== 'visibility') {
@@ -611,10 +654,10 @@ while ((match = exampleRegex.exec(examplesSrc)) !== null) {
let testMethodName = match[1],
indentation = match[2],
exampleCode = match[3];
-
+
// Trim leading whitespace from the example code.
exampleCode = exampleCode.replace(new RegExp('^' + indentation, 'gm'), '');
-
+
examples[testMethodName] = exampleCode;
}
@@ -626,13 +669,13 @@ global.guideExample = function (guide, exampleId, os) {
console.error(`MGLDocumentationExampleTests.test${testMethodName}() not found.`);
process.exit(1);
}
-
+
// Resolve conditional compilation blocks.
example = example.replace(/^(\s*)#if\s+os\((iOS|macOS)\)\n([^]*?)(?:^\1#else\n([^]*?))?^\1#endif\b\n?/gm,
function (m, indentation, ifOs, ifCase, elseCase) {
return (os === ifOs ? ifCase : elseCase).replace(new RegExp('^ ', 'gm'), '');
}).replace(/\n$/, '');
-
+
return '```swift\n' + example + '\n```';
};
diff --git a/platform/darwin/scripts/style-spec-overrides-v8.json b/platform/darwin/scripts/style-spec-overrides-v8.json
index a594ef2957..67a6641fe7 100644
--- a/platform/darwin/scripts/style-spec-overrides-v8.json
+++ b/platform/darwin/scripts/style-spec-overrides-v8.json
@@ -1,4 +1,10 @@
{
+ "light-cocoa-doc": "An `MGLLight` object represents the light source for extruded geometries in `MGLStyle`.",
+ "light": {
+ "position": {
+ "doc": "Position of the `MGLLight` source relative to lit (extruded) geometries, in a `MGLSphericalPosition` struct [radial coordinate, azimuthal angle, polar angle] where radial indicates the distance from the center of the base of an object to its light, azimuthal indicates the position of the light relative to 0° (0° when `MGLLight.anchor` is set to `MGLLightAnchorViewport` corresponds to the top of the viewport, or 0° when `MGLLight.anchor` is set to `MGLLightAnchorMap` corresponds to due north, and degrees proceed clockwise), and polar indicates the height of the light (from 0°, directly above, to 180°, directly below)."
+ }
+ },
"layer": {
"type": {
"values": {
diff --git a/platform/darwin/src/CFHandle.hpp b/platform/darwin/src/CFHandle.hpp
new file mode 100644
index 0000000000..edcc9aafdf
--- /dev/null
+++ b/platform/darwin/src/CFHandle.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+/*
+ CFHandle is a minimal wrapper designed to hold and release CoreFoundation-style handles
+ It is non-transferrable: wrap it in something like a unique_ptr if you need to pass it around,
+ or just use unique_ptr with a custom deleter.
+ CFHandle has no special treatment for null handles -- be careful not to let it hold a null
+ handle if the behavior of the Releaser isn't defined for null.
+
+ ex:
+ using CFDataHandle = CFHandle<CFDataRef, CFTypeRef, CFRelease>;
+
+ CFDataHandle data(CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault, reinterpret_cast<const unsigned char*>(source.data()), source.size(),
+ kCFAllocatorNull));
+*/
+
+namespace {
+
+template <typename HandleType, typename ReleaserArgumentType, void (*Releaser)(ReleaserArgumentType)>
+struct CFHandle {
+ CFHandle(HandleType handle_): handle(handle_) {}
+ ~CFHandle() { Releaser(handle); }
+ HandleType operator*() { return handle; }
+ operator bool() { return handle; }
+private:
+ HandleType handle;
+};
+
+} // namespace
+
diff --git a/platform/darwin/src/MGLAbstractShapeSource.h b/platform/darwin/src/MGLAbstractShapeSource.h
new file mode 100644
index 0000000000..3b35986b3f
--- /dev/null
+++ b/platform/darwin/src/MGLAbstractShapeSource.h
@@ -0,0 +1,99 @@
+#import "MGLSource.h"
+
+/**
+ Options for `MGLShapeSource` objects.
+ */
+typedef NSString *MGLShapeSourceOption NS_STRING_ENUM;
+
+/**
+ An `NSNumber` object containing a Boolean enabling or disabling clustering.
+ If the `shape` property contains point shapes, setting this option to
+ `YES` clusters the points by radius into groups. The default value is `NO`.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-cluster"><code>cluster</code></a>
+ source property in the Mapbox Style Specification.
+
+ This option only affects point features within a shape source.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClustered;
+
+/**
+ An `NSNumber` object containing an integer; specifies the radius of each
+ cluster if clustering is enabled. A value of 512 produces a radius equal to
+ the width of a tile. The default value is 50.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
+
+/**
+ An `NSNumber` object containing an integer; specifies the maximum zoom level at
+ which to cluster points if clustering is enabled. Defaults to one zoom level
+ less than the value of `MGLShapeSourceOptionMaximumZoomLevel` so that, at the
+ maximum zoom level, the shapes are not clustered.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-clusterMaxZoom"><code>clusterMaxZoom</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering;
+
+/**
+ An `NSNumber` object containing an integer; specifies the minimum zoom level at
+ which to create vector tiles. The default value is 0.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-minzoom"><code>minzoom</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel;
+
+/**
+ An `NSNumber` object containing an integer; specifies the maximum zoom level at
+ which to create vector tiles. A greater value produces greater detail at high
+ zoom levels. The default value is 18.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-maxzoom"><code>maxzoom</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel;
+
+/**
+ An `NSNumber` object containing an integer; specifies the size of the tile
+ buffer on each side. A value of 0 produces no buffer. A value of 512 produces a
+ buffer as wide as the tile itself. Larger values produce fewer rendering
+ artifacts near tile edges and slower performance. The default value is 128.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-buffer"><code>buffer</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer;
+
+/**
+ An `NSNumber` object containing a double; specifies the Douglas-Peucker
+ simplification tolerance. A greater value produces simpler geometries and
+ improves performance. The default value is 0.375.
+
+ This attribute corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-tolerance"><code>tolerance</code></a>
+ source property in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
+
+/**
+ `MGLAbstractShapeSource` is an abstract base class for map content sources that
+ supply vector shapes to be shown on the map. A shape source is added to an
+ `MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
+ layer defines the appearance of any content supplied by the shape source.
+
+
+ Do not create instances of this class directly, and do not create your own
+ subclasses of this class. Instead, create instances of `MGLShapeSource` or
+ `MGLComputedShapeSource`.
+ */
+MGL_EXPORT
+@interface MGLAbstractShapeSource : MGLSource
+
+
+@end
diff --git a/platform/darwin/src/MGLAbstractShapeSource.mm b/platform/darwin/src/MGLAbstractShapeSource.mm
new file mode 100644
index 0000000000..755d040387
--- /dev/null
+++ b/platform/darwin/src/MGLAbstractShapeSource.mm
@@ -0,0 +1,117 @@
+#import "MGLAbstractShapeSource.h"
+#import "MGLAbstractShapeSource_Private.h"
+
+const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
+const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius";
+const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
+const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel";
+const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
+const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel";
+const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance";
+
+@interface MGLAbstractShapeSource ()
+
+@end
+
+@implementation MGLAbstractShapeSource
+
+@end
+
+mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
+ auto geoJSONOptions = mbgl::style::GeoJSONOptions();
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ geoJSONOptions.minzoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ geoJSONOptions.maxzoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
+ }
+ geoJSONOptions.buffer = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
+ }
+ geoJSONOptions.tolerance = value.doubleValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionClusterRadius]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClusterRadius must be an NSNumber."];
+ }
+ geoJSONOptions.clusterRadius = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevelForClustering]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
+ }
+ geoJSONOptions.clusterMaxZoom = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionClustered]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionClustered must be an NSNumber."];
+ }
+ geoJSONOptions.cluster = value.boolValue;
+ }
+
+ return geoJSONOptions;
+}
+
+mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
+ mbgl::style::CustomGeometrySource::Options sourceOptions;
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
+ }
+ sourceOptions.zoomRange.min = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
+ }
+ sourceOptions.zoomRange.max = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.buffer = value.integerValue;
+ }
+
+ if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
+ if (![value isKindOfClass:[NSNumber class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
+ }
+ sourceOptions.tileOptions.tolerance = value.doubleValue;
+ }
+ return sourceOptions;
+}
diff --git a/platform/darwin/src/MGLAbstractShapeSource_Private.h b/platform/darwin/src/MGLAbstractShapeSource_Private.h
new file mode 100644
index 0000000000..ddde55b149
--- /dev/null
+++ b/platform/darwin/src/MGLAbstractShapeSource_Private.h
@@ -0,0 +1,22 @@
+#import "MGLAbstractShapeSource.h"
+
+#import "MGLFoundation.h"
+#import "MGLTypes.h"
+#import "MGLShape.h"
+
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLAbstractShapeSource (Private)
+
+MGL_EXPORT
+
+mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
+
+MGL_EXPORT
+mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
+
+@end
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLAccountManager.m b/platform/darwin/src/MGLAccountManager.m
index 0f5d033031..729caeaa31 100644
--- a/platform/darwin/src/MGLAccountManager.m
+++ b/platform/darwin/src/MGLAccountManager.m
@@ -39,7 +39,7 @@
}
static dispatch_once_t onceToken;
static MGLAccountManager *_sharedManager;
- void (^setupBlock)() = ^{
+ void (^setupBlock)(void) = ^{
dispatch_once(&onceToken, ^{
_sharedManager = [[self alloc] init];
});
@@ -55,7 +55,7 @@
}
+ (BOOL)mapboxMetricsEnabledSettingShownInApp {
- NSLog(@"mapboxMetricsEnabledSettingShownInApp is no longer necessary; the Mapbox iOS SDK has changed to always provide a telemetry setting in-app.");
+ NSLog(@"mapboxMetricsEnabledSettingShownInApp is no longer necessary; the Mapbox Maps SDK for iOS has changed to always provide a telemetry setting in-app.");
return YES;
}
diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm
index 770aeb25e7..ce63a25422 100644
--- a/platform/darwin/src/MGLAttributionInfo.mm
+++ b/platform/darwin/src/MGLAttributionInfo.mm
@@ -151,6 +151,7 @@
[NSURLQueryItem queryItemWithName:@"owner" value:stylePathComponents[1]],
[NSURLQueryItem queryItemWithName:@"id" value:stylePathComponents[2]],
[NSURLQueryItem queryItemWithName:@"access_token" value:[MGLAccountManager accessToken]],
+ [NSURLQueryItem queryItemWithName:@"map_sdk_version" value:[NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"]],
]];
}
}
diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h
index 789ff7a258..ea95f6beef 100644
--- a/platform/darwin/src/MGLCircleStyleLayer.h
+++ b/platform/darwin/src/MGLCircleStyleLayer.h
@@ -8,6 +8,23 @@
NS_ASSUME_NONNULL_BEGIN
/**
+ Orientation of circle when map is pitched.
+
+ Values of this type are used in the `MGLCircleStyleLayer.circlePitchAlignment`
+ property.
+ */
+typedef NS_ENUM(NSUInteger, MGLCirclePitchAlignment) {
+ /**
+ The circle is aligned to the plane of the map.
+ */
+ MGLCirclePitchAlignmentMap,
+ /**
+ The circle is aligned to the plane of the viewport.
+ */
+ MGLCirclePitchAlignmentViewport,
+};
+
+/**
Controls the scaling behavior of the circle when the map is pitched.
Values of this type are used in the `MGLCircleStyleLayer.circleScaleAlignment`
@@ -221,6 +238,21 @@ MGL_EXPORT
@property (nonatomic) MGLTransition circleOpacityTransition;
/**
+ 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.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of
+ `MGLInterpolationModeInterval`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *circlePitchAlignment;
+
+/**
Circle radius.
This property is measured in points.
@@ -491,6 +523,19 @@ MGL_EXPORT
#pragma mark Working with Circle Style Layer Attribute Values
/**
+ Creates a new value object containing the given `MGLCirclePitchAlignment` enumeration.
+
+ @param circlePitchAlignment The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLCirclePitchAlignment:(MGLCirclePitchAlignment)circlePitchAlignment;
+
+/**
+ The `MGLCirclePitchAlignment` enumeration representation of the value.
+ */
+@property (readonly) MGLCirclePitchAlignment MGLCirclePitchAlignmentValue;
+
+/**
Creates a new value object containing the given `MGLCircleScaleAlignment` enumeration.
@param circleScaleAlignment The value for the new object.
diff --git a/platform/darwin/src/MGLCircleStyleLayer.mm b/platform/darwin/src/MGLCircleStyleLayer.mm
index 42961f2e12..71ae37035e 100644
--- a/platform/darwin/src/MGLCircleStyleLayer.mm
+++ b/platform/darwin/src/MGLCircleStyleLayer.mm
@@ -13,6 +13,11 @@
namespace mbgl {
+ MBGL_DEFINE_ENUM(MGLCirclePitchAlignment, {
+ { MGLCirclePitchAlignmentMap, "map" },
+ { MGLCirclePitchAlignmentViewport, "viewport" },
+ });
+
MBGL_DEFINE_ENUM(MGLCircleScaleAlignment, {
{ MGLCircleScaleAlignmentMap, "map" },
{ MGLCircleScaleAlignmentViewport, "viewport" },
@@ -187,6 +192,23 @@ namespace mbgl {
return transition;
}
+- (void)setCirclePitchAlignment:(MGLStyleValue<NSValue *> *)circlePitchAlignment {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumPropertyValue(circlePitchAlignment);
+ self.rawLayer->setCirclePitchAlignment(mbglValue);
+}
+
+- (MGLStyleValue<NSValue *> *)circlePitchAlignment {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getCirclePitchAlignment();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultCirclePitchAlignment());
+ }
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLCirclePitchAlignment>().toEnumStyleValue(propertyValue);
+}
+
- (void)setCircleRadius:(MGLStyleValue<NSNumber *> *)circleRadius {
MGLAssertStyleLayerIsValid();
@@ -421,6 +443,16 @@ namespace mbgl {
@implementation NSValue (MGLCircleStyleLayerAdditions)
++ (NSValue *)valueWithMGLCirclePitchAlignment:(MGLCirclePitchAlignment)circlePitchAlignment {
+ return [NSValue value:&circlePitchAlignment withObjCType:@encode(MGLCirclePitchAlignment)];
+}
+
+- (MGLCirclePitchAlignment)MGLCirclePitchAlignmentValue {
+ MGLCirclePitchAlignment circlePitchAlignment;
+ [self getValue:&circlePitchAlignment];
+ return circlePitchAlignment;
+}
+
+ (NSValue *)valueWithMGLCircleScaleAlignment:(MGLCircleScaleAlignment)circleScaleAlignment {
return [NSValue value:&circleScaleAlignment withObjCType:@encode(MGLCircleScaleAlignment)];
}
diff --git a/platform/darwin/src/MGLComputedShapeSource.h b/platform/darwin/src/MGLComputedShapeSource.h
new file mode 100644
index 0000000000..f90f2c94b1
--- /dev/null
+++ b/platform/darwin/src/MGLComputedShapeSource.h
@@ -0,0 +1,113 @@
+#import "MGLAbstractShapeSource.h"
+
+#import "MGLFoundation.h"
+#import "MGLGeometry.h"
+#import "MGLTypes.h"
+#import "MGLShape.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol MGLFeature;
+
+/**
+ Data source for `MGLComputedShapeSource`. This protocol defines two optional methods for fetching
+ data, one based on tile coordinates, and one based on a bounding box. Classes that implement this
+ protocol must implement one, and only one of the methods. Methods on this protocol will not be
+ called on main thread, they will be called on the caller's `requestQueue`.
+ */
+@protocol MGLComputedShapeSourceDataSource <NSObject>
+
+@optional
+/**
+ Fetch features for a tile. This method will not be invoked on the main queue, it
+ will be invoked on the caller's `requestQueue`.
+ @param x Tile X coordinate.
+ @param y Tile Y coordinate.
+ @param zoomLevel Tile zoom level.
+ */
+- (NSArray<MGLShape <MGLFeature> *>*)featuresInTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel;
+
+/**
+ Fetch features for a tile. This method will not be invoked on the main queue, it
+ will be invoked on the caller's `requestQueue`.
+ @param bounds The bounds to fetch data for.
+ @param zoomLevel Tile zoom level.
+ */
+- (NSArray<MGLShape <MGLFeature> *>*)featuresInCoordinateBounds:(MGLCoordinateBounds)bounds zoomLevel:(NSUInteger)zoomLevel;
+
+@end
+
+/**
+ A source for vector data that is fetched one tile at a time. Useful for sources that are
+ too large to fit in memory, or are already divided into tiles, but not in Mapbox Vector Tile format.
+
+ Supported options are `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and `MGLShapeSourceOptionSimplificationTolerance.` This source does
+ not support clustering.
+ */
+MGL_EXPORT
+@interface MGLComputedShapeSource : MGLAbstractShapeSource
+
+/**
+ Returns a custom shape data source initialized with an identifier, and a
+ dictionary of options for the source according to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">style
+ specification</a>.
+
+ @param identifier A string that uniquely identifies the source.
+ @param options An `NSDictionary` of options for this source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
+
+/**
+ Returns a custom shape data source initialized with an identifier, data source, and a
+ dictionary of options for the source according to the
+ <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson">style
+ specification</a>.
+
+ @param identifier A string that uniquely identifies the source.
+ @param options An `NSDictionary` of options for this source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options;
+
+/**
+ Invalidates all the features and properties intersecting with or contained in
+ the specified bounds. New fetch requests will immediately be invoked on the
+ `MGLComputedShapeSourceDataSource`.
+ @param bounds Coordinate bounds to invalidate.
+ */
+- (void) invalidateBounds:(MGLCoordinateBounds)bounds;
+
+/**
+ Invalidates all the feautres and properties of a given tile. A new fetch request
+ will immediately be invoked on the `MGLComputedShapeSourceDataSource`.
+ @param x Tile X coordinate.
+ @param y Tile Y coordinate.
+ @param zoomLevel Tile zoom level.
+ */
+- (void) invalidateTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel;
+
+/**
+ Set a new set of features for a tile. This method can be invkoed from background threads.
+ For best performance, use this method only to update tiles that have already been requested
+ through `MGLComputedShapeSourceDataSource.`
+ @param features Features for the tile.
+ @param x Tile X coordinate.
+ @param y Tile Y coordinate.
+ @param zoomLevel Tile zoom level.
+ */
+- (void) setFeatures:(NSArray<MGLShape <MGLFeature> *>*)features inTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel;
+
+/**
+ An object that implements the `MGLComputedShapeSourceDataSource` protocol that will be queried for tile data.
+ */
+@property (nonatomic, weak, nullable) id<MGLComputedShapeSourceDataSource> dataSource;
+
+/**
+ A queue that calls to the data source will be made on.
+ */
+@property (nonatomic, readonly) NSOperationQueue *requestQueue;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm
new file mode 100644
index 0000000000..0a3c92bb97
--- /dev/null
+++ b/platform/darwin/src/MGLComputedShapeSource.mm
@@ -0,0 +1,169 @@
+#import "MGLComputedShapeSource.h"
+
+#import "MGLMapView_Private.h"
+#import "MGLSource_Private.h"
+#import "MGLShape_Private.h"
+#import "MGLAbstractShapeSource_Private.h"
+#import "MGLGeometry_Private.h"
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/geojson.hpp>
+
+@interface MGLComputedShapeSource () {
+ std::unique_ptr<mbgl::style::CustomGeometrySource> _pendingSource;
+}
+
+@property (nonatomic, readwrite) NSDictionary *options;
+@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForTile;
+@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForBounds;
+
+@end
+
+@interface MGLComputedShapeSourceFetchOperation : NSOperation
+
+@property (nonatomic, readonly) uint8_t z;
+@property (nonatomic, readonly) uint32_t x;
+@property (nonatomic, readonly) uint32_t y;
+@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForTile;
+@property (nonatomic, assign) BOOL dataSourceImplementsFeaturesForBounds;
+@property (nonatomic, weak, nullable) id<MGLComputedShapeSourceDataSource> dataSource;
+@property (nonatomic, nullable) mbgl::style::CustomGeometrySource *rawSource;
+
+- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileId;
+
+@end
+
+@implementation MGLComputedShapeSourceFetchOperation
+
+- (instancetype)initForSource:(MGLComputedShapeSource*)source tile:(const mbgl::CanonicalTileID&)tileID {
+ self = [super init];
+ _z = tileID.z;
+ _x = tileID.x;
+ _y = tileID.y;
+ _dataSourceImplementsFeaturesForTile = source.dataSourceImplementsFeaturesForTile;
+ _dataSourceImplementsFeaturesForBounds = source.dataSourceImplementsFeaturesForBounds;
+ _dataSource = source.dataSource;
+ mbgl::style::CustomGeometrySource *rawSource = static_cast<mbgl::style::CustomGeometrySource *>(source.rawSource);
+ _rawSource = rawSource;
+ return self;
+}
+
+- (void)main {
+ if ([self isCancelled]) {
+ return;
+ }
+
+ NSArray<MGLShape <MGLFeature> *> *data;
+ if(!self.dataSource) {
+ data = nil;
+ } else if(self.dataSourceImplementsFeaturesForTile) {
+ data = [self.dataSource featuresInTileAtX:self.x
+ y:self.y
+ zoomLevel:self.z];
+ } else {
+ mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID(self.z, self.x, self.y);
+ mbgl::LatLngBounds tileBounds = mbgl::LatLngBounds(tileID);
+ data = [self.dataSource featuresInCoordinateBounds:MGLCoordinateBoundsFromLatLngBounds(tileBounds)
+ zoomLevel:self.z];
+ }
+
+ if(![self isCancelled]) {
+ mbgl::FeatureCollection featureCollection;
+ featureCollection.reserve(data.count);
+ for (MGLShape <MGLFeature> * feature in data) {
+ mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>();
+ featureCollection.push_back(geoJsonObject);
+ }
+ const auto geojson = mbgl::GeoJSON{featureCollection};
+ if(![self isCancelled] && self.rawSource) {
+ self.rawSource->setTileData(mbgl::CanonicalTileID(self.z, self.x, self.y), geojson);
+ }
+ }
+}
+
+- (void)cancel {
+ [super cancel];
+ self.rawSource = NULL;
+}
+
+@end
+
+@implementation MGLComputedShapeSource
+
+- (instancetype)initWithIdentifier:(NSString *)identifier options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+ NSOperationQueue *requestQueue = [[NSOperationQueue alloc] init];
+ requestQueue.name = [NSString stringWithFormat:@"mgl.MGLComputedShapeSource.%@", identifier];
+ requestQueue.qualityOfService = NSQualityOfServiceUtility;
+ requestQueue.maxConcurrentOperationCount = 4;
+
+ auto sourceOptions = MBGLCustomGeometrySourceOptionsFromDictionary(options);
+ sourceOptions.fetchTileFunction = ^void(const mbgl::CanonicalTileID& tileID) {
+ NSOperation *operation = [[MGLComputedShapeSourceFetchOperation alloc] initForSource:self tile:tileID];
+ [requestQueue addOperation:operation];
+ };
+
+ sourceOptions.cancelTileFunction = ^void(const mbgl::CanonicalTileID& tileID) {
+ for (MGLComputedShapeSourceFetchOperation *operation in requestQueue.operations) {
+ if (operation.x == tileID.x && operation.y == tileID.y && operation.z == tileID.z) {
+ [operation cancel];
+ }
+ }
+ };
+
+ auto source = std::make_unique<mbgl::style::CustomGeometrySource>(identifier.UTF8String, sourceOptions);
+
+ if (self = [super initWithPendingSource:std::move(source)]) {
+ _requestQueue = requestQueue;
+ }
+ return self;
+}
+
+- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id<MGLComputedShapeSourceDataSource>)dataSource options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+ if (self = [self initWithIdentifier:identifier options:options]) {
+ [self setDataSource:dataSource];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self.requestQueue cancelAllOperations];
+}
+
+- (void)setFeatures:(NSArray<MGLShape <MGLFeature> *>*)features inTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)zoomLevel {
+ mbgl::CanonicalTileID tileID = mbgl::CanonicalTileID((uint8_t)zoomLevel, (uint32_t)x, (uint32_t)y);
+ mbgl::FeatureCollection featureCollection;
+ featureCollection.reserve(features.count);
+ for (MGLShape <MGLFeature> * feature in features) {
+ mbgl::Feature geoJsonObject = [feature geoJSONObject].get<mbgl::Feature>();
+ featureCollection.push_back(geoJsonObject);
+ }
+ const auto geojson = mbgl::GeoJSON{featureCollection};
+ static_cast<mbgl::style::CustomGeometrySource *>(self.rawSource)->setTileData(tileID, geojson);
+}
+
+- (void)setDataSource:(id<MGLComputedShapeSourceDataSource>)dataSource {
+ [self.requestQueue cancelAllOperations];
+ // Check which method the datasource implements, to avoid having to check for each tile
+ self.dataSourceImplementsFeaturesForTile = [dataSource respondsToSelector:@selector(featuresInTileAtX:y:zoomLevel:)];
+ self.dataSourceImplementsFeaturesForBounds = [dataSource respondsToSelector:@selector(featuresInCoordinateBounds:zoomLevel:)];
+
+ if(!self.dataSourceImplementsFeaturesForBounds && !self.dataSourceImplementsFeaturesForTile) {
+ [NSException raise:@"Invalid Datasource" format:@"Datasource does not implement any MGLComputedShapeSourceDataSource methods"];
+ } else if(self.dataSourceImplementsFeaturesForBounds && self.dataSourceImplementsFeaturesForTile) {
+ [NSException raise:@"Invalid Datasource" format:@"Datasource implements multiple MGLComputedShapeSourceDataSource methods"];
+ }
+
+ _dataSource = dataSource;
+}
+
+- (void) invalidateBounds:(MGLCoordinateBounds)bounds {
+ ((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateRegion(MGLLatLngBoundsFromCoordinateBounds(bounds));
+}
+
+- (void) invalidateTileAtX:(NSUInteger)x y:(NSUInteger)y zoomLevel:(NSUInteger)z {
+ ((mbgl::style::CustomGeometrySource *)self.rawSource)->invalidateTile(mbgl::CanonicalTileID(z, (unsigned int)x, (unsigned int)y));
+}
+
+@end
diff --git a/platform/darwin/src/MGLConversion.h b/platform/darwin/src/MGLConversion.h
index d6363b28eb..0d18d4e716 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,147 @@ 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&, Fn&&) {
+#pragma clang diagnostic pop
+ // Not implemented (unneeded for MGLStyleFunction conversion).
+ NSCAssert(NO, @"eachMember not implemented");
+ 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 +154,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 491c89b608..a13821cf96 100644
--- a/platform/darwin/src/MGLFeature.h
+++ b/platform/darwin/src/MGLFeature.h
@@ -18,15 +18,23 @@ NS_ASSUME_NONNULL_BEGIN
You can add custom data to display on the map by creating feature objects and
adding them to an `MGLShapeSource` using the
`-[MGLShapeSource initWithIdentifier:shape:options:]` method or
- `MGLShapeSource.shape` property. Similarly, you can add `MGLPointFeature`,
- `MGLPolylineFeature`, and `MGLPolygonFeature` objects to the map as annotations
- using `-[MGLMapView addAnnotations:]` and related methods.
+ `MGLShapeSource.shape` property.
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.
+
+ While it is possible to add `MGLFeature`-conforming objects to the map as
+ annotations using `-[MGLMapView addAnnotations:]` and related methods, doing so
+ has trade-offs:
+
+ - Features added as annotations will not have `identifier` or `attributes`
+ properties when used with feature querying.
+
+ - Features added as annotations become interactive. Taps and selection can be
+ handled in `-[MGLMapViewDelegate mapView:didSelectAnnotation:]`.
*/
@protocol MGLFeature <MGLAnnotation>
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index e169ee19bb..033052bda8 100644
--- a/platform/darwin/src/MGLFeature.mm
+++ b/platform/darwin/src/MGLFeature.mm
@@ -7,11 +7,11 @@
#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 <mbgl/util/geometry.hpp>
@@ -42,6 +42,15 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, coordinate = %f, %f, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ self.coordinate.latitude, self.coordinate.longitude,
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLPolylineFeature ()
@@ -68,6 +77,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ (unsigned long)[self pointCount],
+ MGLStringFromCoordinateBounds(self.overlayBounds),
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLPolygonFeature ()
@@ -94,6 +113,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ (unsigned long)[self pointCount],
+ MGLStringFromCoordinateBounds(self.overlayBounds),
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLPointCollectionFeature ()
@@ -146,6 +175,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ (unsigned long)self.polylines.count,
+ MGLStringFromCoordinateBounds(self.overlayBounds),
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLMultiPolygonFeature ()
@@ -172,6 +211,16 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
return mbglFeature({[self geometryObject]}, identifier, self.attributes);
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; identifier = %@, count = %lu, bounds = %@, attributes = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.identifier ? [NSString stringWithFormat:@"\"%@\"", self.identifier] : self.identifier,
+ (unsigned long)self.polygons.count,
+ MGLStringFromCoordinateBounds(self.overlayBounds),
+ self.attributes.count ? self.attributes : @"none"];
+}
+
@end
@interface MGLShapeCollectionFeature ()
diff --git a/platform/darwin/src/MGLForegroundStyleLayer.h b/platform/darwin/src/MGLForegroundStyleLayer.h
index 16a973630e..bcd323fb99 100644
--- a/platform/darwin/src/MGLForegroundStyleLayer.h
+++ b/platform/darwin/src/MGLForegroundStyleLayer.h
@@ -11,9 +11,10 @@ NS_ASSUME_NONNULL_BEGIN
`MGLForegroundStyleLayer` is an abstract superclass for style layers whose
content is defined by an `MGLSource` object.
- Do not create instances of this class directly, and do not create your own
- subclasses of this class. Instead, create instances of `MGLRasterStyleLayer`
- and the concrete subclasses of `MGLVectorStyleLayer`.
+ 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.
*/
MGL_EXPORT
@interface MGLForegroundStyleLayer : MGLStyleLayer
diff --git a/platform/darwin/src/MGLGeometry.h b/platform/darwin/src/MGLGeometry.h
index 3a3e59fb3e..7c68033abf 100644
--- a/platform/darwin/src/MGLGeometry.h
+++ b/platform/darwin/src/MGLGeometry.h
@@ -7,7 +7,7 @@
NS_ASSUME_NONNULL_BEGIN
/** Defines the area spanned by an `MGLCoordinateBounds`. */
-typedef struct MGLCoordinateSpan {
+typedef struct __attribute__((objc_boxable)) MGLCoordinateSpan {
/** Latitudes spanned by an `MGLCoordinateBounds`. */
CLLocationDegrees latitudeDelta;
/** Longitudes spanned by an `MGLCoordinateBounds`. */
@@ -38,7 +38,7 @@ NS_INLINE BOOL MGLCoordinateSpanEqualToCoordinateSpan(MGLCoordinateSpan span1, M
extern MGL_EXPORT const MGLCoordinateSpan MGLCoordinateSpanZero;
/** A rectangular area as measured on a two-dimensional map projection. */
-typedef struct MGLCoordinateBounds {
+typedef struct __attribute__((objc_boxable)) MGLCoordinateBounds {
/** Coordinate at the southwest corner. */
CLLocationCoordinate2D sw;
/** Coordinate at the northeast corner. */
diff --git a/platform/darwin/src/MGLGeometry.mm b/platform/darwin/src/MGLGeometry.mm
index 1540a3a741..715a70f0b8 100644
--- a/platform/darwin/src/MGLGeometry.mm
+++ b/platform/darwin/src/MGLGeometry.mm
@@ -4,6 +4,10 @@
#import <mbgl/util/projection.hpp>
+#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+#import <Cocoa/Cocoa.h>
+#endif
+
/** Vertical field of view, measured in degrees, for determining the altitude
of the viewpoint.
@@ -57,3 +61,48 @@ double MGLZoomLevelForAltitude(CLLocationDistance altitude, CGFloat pitch, CLLoc
CGFloat mapPixelWidthAtZoom = std::cos(MGLRadiansFromDegrees(latitude)) * mbgl::util::M2PI * mbgl::util::EARTH_RADIUS_M / metersPerPixel;
return ::log2(mapPixelWidthAtZoom / mbgl::util::tileSize);
}
+
+MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) {
+ double a = pow(sin((to.latitude - from.latitude) / 2), 2)
+ + pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude);
+
+ return 2 * atan2(sqrt(a), sqrt(1 - a));
+}
+
+MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) {
+ double a = sin(to.longitude - from.longitude) * cos(to.latitude);
+ double b = cos(from.latitude) * sin(to.latitude)
+ - sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude);
+ return atan2(a, b);
+}
+
+MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate,
+ MGLRadianDistance distance,
+ MGLRadianDirection direction) {
+ double otherLatitude = asin(sin(coordinate.latitude) * cos(distance)
+ + cos(coordinate.latitude) * sin(distance) * cos(direction));
+ double otherLongitude = coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude),
+ cos(distance) - sin(coordinate.latitude) * sin(otherLatitude));
+ return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude);
+}
+
+CLLocationDirection MGLDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate, CLLocationCoordinate2D secondCoordinate) {
+ // Ported from https://github.com/mapbox/turf-swift/blob/857e2e8060678ef4a7a9169d4971b0788fdffc37/Turf/Turf.swift#L23-L31
+ MGLRadianCoordinate2D firstRadianCoordinate = MGLRadianCoordinateFromLocationCoordinate(firstCoordinate);
+ MGLRadianCoordinate2D secondRadianCoordinate = MGLRadianCoordinateFromLocationCoordinate(secondCoordinate);
+
+ CGFloat a = sin(secondRadianCoordinate.longitude - firstRadianCoordinate.longitude) * cos(secondRadianCoordinate.latitude);
+ CGFloat b = (cos(firstRadianCoordinate.latitude) * sin(secondRadianCoordinate.latitude)
+ - sin(firstRadianCoordinate.latitude) * cos(secondRadianCoordinate.latitude) * cos(secondRadianCoordinate.longitude - firstRadianCoordinate.longitude));
+ MGLRadianDirection radianDirection = atan2(a, b);
+ return radianDirection * 180 / M_PI;
+}
+
+CGPoint MGLPointRounded(CGPoint point) {
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+ CGFloat scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [UIScreen mainScreen].nativeScale : [UIScreen mainScreen].scale;
+#elif TARGET_OS_MAC
+ CGFloat scaleFactor = [NSScreen mainScreen].backingScaleFactor;
+#endif
+ return CGPointMake(round(point.x * scaleFactor) / scaleFactor, round(point.y * scaleFactor) / scaleFactor);
+}
diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h
index 88fcf5b576..8b9c6c2327 100644
--- a/platform/darwin/src/MGLGeometry_Private.h
+++ b/platform/darwin/src/MGLGeometry_Private.h
@@ -105,36 +105,26 @@ NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateFromLocationCoordinate(CLLoca
MGLRadiansFromDegrees(locationCoordinate.longitude));
}
-/*
+/**
Returns the distance in radians given two coordinates.
*/
-NS_INLINE MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to)
-{
- double a = pow(sin((to.latitude - from.latitude) / 2), 2)
- + pow(sin((to.longitude - from.longitude) / 2), 2) * cos(from.latitude) * cos(to.latitude);
-
- return 2 * atan2(sqrt(a), sqrt(1 - a));
-}
+MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to);
-/*
+/**
Returns direction in radians given two coordinates.
*/
-NS_INLINE MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) {
- double a = sin(to.longitude - from.longitude) * cos(to.latitude);
- double b = cos(from.latitude) * sin(to.latitude)
- - sin(from.latitude) * cos(to.latitude) * cos(to.longitude - from.longitude);
- return atan2(a, b);
-}
+MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to);
+
+/**
+ Returns a coordinate at a given distance and direction away from coordinate.
+ */
+MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate,
+ MGLRadianDistance distance,
+ MGLRadianDirection direction);
-/*
- Returns coordinate at a given distance and direction away from coordinate.
+/**
+ Returns the direction from one coordinate to another.
*/
-NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoordinate2D coordinate,
- MGLRadianDistance distance,
- MGLRadianDirection direction) {
- double otherLatitude = asin(sin(coordinate.latitude) * cos(distance)
- + cos(coordinate.latitude) * sin(distance) * cos(direction));
- double otherLongitude = coordinate.longitude + atan2(sin(direction) * sin(distance) * cos(coordinate.latitude),
- cos(distance) - sin(coordinate.latitude) * sin(otherLatitude));
- return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude);
-}
+CLLocationDirection MGLDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate, CLLocationCoordinate2D secondCoordinate);
+
+CGPoint MGLPointRounded(CGPoint point);
diff --git a/platform/darwin/src/MGLImageSource.h b/platform/darwin/src/MGLImageSource.h
index 21487d9739..5088f6bac0 100644
--- a/platform/darwin/src/MGLImageSource.h
+++ b/platform/darwin/src/MGLImageSource.h
@@ -38,8 +38,11 @@ MGL_EXPORT
bottomLeft: CLLocationCoordinate2D(latitude: 37.936, longitude: -80.425),
bottomRight: CLLocationCoordinate2D(latitude: 37.936, longitude: -71.516),
topRight: CLLocationCoordinate2D(latitude: 46.437, longitude: -71.516))
- let source = MGLImageSource(identifier: "radar", coordinateQuad: coordinates, url: URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif")!)
+ let source = MGLImageSource(identifier: "radar-source", coordinateQuad: coordinates, url: URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/radar.gif")!)
mapView.style?.addSource(source)
+
+ let layer = MGLRasterStyleLayer(identifier: "radar-layer", source: source)
+ style.addLayer(layer)
```
*/
MGL_EXPORT
diff --git a/platform/darwin/src/MGLImageSource.mm b/platform/darwin/src/MGLImageSource.mm
index 0a2dc2713f..351247e901 100644
--- a/platform/darwin/src/MGLImageSource.mm
+++ b/platform/darwin/src/MGLImageSource.mm
@@ -60,10 +60,9 @@
- (void)setImage:(MGLImage *)image {
if (image != nullptr) {
- mbgl::UnassociatedImage unassociatedImage = mbgl::util::unpremultiply(image.mgl_premultipliedImage);
- self.rawSource->setImage(std::move(unassociatedImage));
+ self.rawSource->setImage(image.mgl_premultipliedImage);
} else {
- self.rawSource->setImage(mbgl::UnassociatedImage({0,0}));
+ self.rawSource->setImage(mbgl::PremultipliedImage({0,0}));
}
_image = image;
}
diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h
index ef9811bd33..55b789f043 100644
--- a/platform/darwin/src/MGLLight.h
+++ b/platform/darwin/src/MGLLight.h
@@ -1,3 +1,6 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
#import <CoreLocation/CoreLocation.h>
#import "MGLFoundation.h"
@@ -5,13 +8,19 @@
NS_ASSUME_NONNULL_BEGIN
-
-/** Options to specify extruded geometries are lit relative to the map or viewport. */
+/**
+ Whether extruded geometries are lit relative to the map or viewport.
+ */
typedef NS_ENUM(NSUInteger, MGLLightAnchor) {
- /** The position of the light source is aligned to the rotation of the map. */
+ /**
+ The position of the light source is aligned to the rotation of the map.
+ */
MGLLightAnchorMap,
- /** The position of the light source is aligned to the rotation of the viewport. */
- MGLLightAnchorViewport
+ /**
+ The position of the light source is aligned to the rotation of the
+ viewport.
+ */
+ MGLLightAnchorViewport,
};
/**
@@ -20,7 +29,7 @@ typedef NS_ENUM(NSUInteger, MGLLightAnchor) {
*/
typedef struct MGLSphericalPosition {
/** Distance from the center of the base of an object to its light. */
- CLLocationDistance radial;
+ CGFloat radial;
/** Position of the light relative to 0° (0° when `MGLLight.anchor` is set to viewport corresponds
to the top of the viewport, or 0° when `MGLLight.anchor` is set to map corresponds to due north,
and degrees proceed clockwise). */
@@ -38,7 +47,7 @@ typedef struct MGLSphericalPosition {
@return Returns a `MGLSphericalPosition` struct containing the position attributes.
*/
-NS_INLINE MGLSphericalPosition MGLSphericalPositionMake(CLLocationDistance radial, CLLocationDirection azimuthal, CLLocationDirection polar) {
+NS_INLINE MGLSphericalPosition MGLSphericalPositionMake(CGFloat radial, CLLocationDirection azimuthal, CLLocationDirection polar) {
MGLSphericalPosition position;
position.radial = radial;
position.azimuthal = azimuthal;
@@ -54,8 +63,17 @@ MGL_EXPORT
@interface MGLLight : NSObject
/**
- `anchor` Whether extruded geometries are lit relative to the map or viewport.
+ 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`.
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of
+ `MGLInterpolationModeInterval`
+
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.
@@ -63,14 +81,25 @@ MGL_EXPORT
@property (nonatomic) MGLStyleValue<NSValue *> *anchor;
/**
- Values describing animated transitions to `anchor` property.
- */
-@property (nonatomic) MGLTransition anchorTransition;
-
-
-/**
- Position of the light source relative to lit (extruded) geometries.
+ Position of the `MGLLight` source relative to lit (extruded) geometries, in a
+ `MGLSphericalPosition` struct [radial coordinate, azimuthal angle, polar angle]
+ where radial indicates the distance from the center of the base of an object to
+ its light, azimuthal indicates the position of the light relative to 0° (0°
+ when `MGLLight.anchor` is set to `MGLLightAnchorViewport` corresponds to the
+ top of the viewport, or 0° when `MGLLight.anchor` is set to `MGLLightAnchorMap`
+ 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
+ `MGLSphericalPosition` struct set to 1.15 radial, 210 azimuthal and 30 polar.
+
+ You can set this property to an instance of:
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+
This property corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-position"><code>position</code></a>
light property in the Mapbox Style Specification.
@@ -78,48 +107,87 @@ MGL_EXPORT
@property (nonatomic) MGLStyleValue<NSValue *> *position;
/**
- Values describing animated transitions to `position` property.
- */
-@property (nonatomic) MGLTransition positionTransiton;
+ The transition affecting any changes to this layer’s `position` property.
+ This property corresponds to the `position-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition positionTransition;
#if TARGET_OS_IPHONE
/**
Color tint for lighting extruded geometries.
+ The default value of this property is an `MGLStyleValue` object containing
+ `UIColor.whiteColor`.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+
This property corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-color"><code>color</code></a>
light property in the Mapbox Style Specification.
*/
@property (nonatomic) MGLStyleValue<UIColor *> *color;
#else
-
/**
Color tint for lighting extruded geometries.
+
+ The default value of this property is an `MGLStyleValue` object containing
+ `NSColor.whiteColor`.
+
+ You can set this property to an instance of:
+
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+
+ This property corresponds to the <a
+ href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-color"><code>color</code></a>
+ light property in the Mapbox Style Specification.
*/
@property (nonatomic) MGLStyleValue<NSColor *> *color;
#endif
/**
- Values describing animated transitions to `color` property.
- */
-@property (nonatomic) MGLTransition colorTransiton;
+ The transition affecting any changes to this layer’s `color` property.
+ This property corresponds to the `color-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition colorTransition;
/**
- Intensity of lighting (on a scale from 0 to 1). Higher numbers will present as more extreme contrast.
+ 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`.
+
+ You can set this property to an instance of:
+ * `MGLConstantStyleValue`
+ * `MGLCameraStyleFunction` with an interpolation mode of:
+ * `MGLInterpolationModeExponential`
+ * `MGLInterpolationModeInterval`
+
This property corresponds to the <a
href="https://www.mapbox.com/mapbox-gl-js/style-spec/#light-intensity"><code>intensity</code></a>
light property in the Mapbox Style Specification.
*/
-@property(nonatomic) MGLStyleValue<NSNumber *> *intensity;
+@property (nonatomic) MGLStyleValue<NSNumber *> *intensity;
/**
- Values describing animated transitions to `intensity` property.
- */
+ The transition affecting any changes to this layer’s `intensity` property.
+
+ This property corresponds to the `intensity-transition` property in the style JSON file format.
+*/
@property (nonatomic) MGLTransition intensityTransition;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLLight.h.ejs b/platform/darwin/src/MGLLight.h.ejs
new file mode 100644
index 0000000000..26ecefc3af
--- /dev/null
+++ b/platform/darwin/src/MGLLight.h.ejs
@@ -0,0 +1,100 @@
+<%
+ const properties = locals.properties;
+ const type = locals.type;
+ const doc = locals.doc;
+-%>
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+
+#import <CoreLocation/CoreLocation.h>
+
+#import "MGLFoundation.h"
+#import "MGLStyleValue.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+<% for (const property of properties) { -%>
+<% if (property.type == "enum") { -%>
+/**
+<%- propertyDoc(property.name, property, type, 'enum').wrap(80, 1) %>
+ */
+typedef NS_ENUM(NSUInteger, MGLLight<%- camelize(property.name) %>) {
+<% for (const value in property.values) { -%>
+ /**
+<%- propertyDoc(property.name, property.values[value], type, 'enum').wrap(80, 4+1) %>
+ */
+ MGLLightAnchor<%- camelize(value) %>,
+<% } -%>
+};
+<% } -%>
+<% } -%>
+
+/**
+ A structure containing information about the position of the light source
+ relative to lit geometries.
+ */
+typedef struct 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
+ to the top of the viewport, or 0° when `MGLLight.anchor` is set to map corresponds to due north,
+ and degrees proceed clockwise). */
+ CLLocationDirection azimuthal;
+ /** Indicates the height of the light (from 0°, directly above, to 180°, directly below). */
+ CLLocationDirection polar;
+} MGLSphericalPosition;
+
+/**
+ Creates a new `MGLSphericalPosition` from the given radial, azimuthal, polar.
+
+ @param radial The radial coordinate.
+ @param azimuthal The azimuthal angle.
+ @param polar The polar angle.
+
+ @return Returns a `MGLSphericalPosition` struct containing the position attributes.
+ */
+NS_INLINE MGLSphericalPosition MGLSphericalPositionMake(CGFloat radial, CLLocationDirection azimuthal, CLLocationDirection polar) {
+ MGLSphericalPosition position;
+ position.radial = radial;
+ position.azimuthal = azimuthal;
+ position.polar = polar;
+
+ return position;
+}
+
+/**
+ <%- doc %>
+ */
+MGL_EXPORT
+@interface MGLLight : NSObject
+<% if (properties.length) { -%>
+
+<% for (const property of properties) { -%>
+/**
+<%- propertyDoc(property.name, property, type, 'light').wrap(80, 1) %>
+
+ This property corresponds to the <a
+ 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) %>;
+
+<% if (property.transition) { -%>
+/**
+ The transition affecting any changes to this layer’s `<%- camelizeWithLeadingLowercase(property.name) %>` property.
+
+ This property corresponds to the `<%- originalPropertyName(property) %>-transition` property in the style JSON file format.
+*/
+@property (nonatomic) MGLTransition <%- camelizeWithLeadingLowercase(property.name) %>Transition;
+
+<% } -%>
+<% if (property.original) { -%>
+@property (nonatomic<% if (!property.required) { %>, null_resettable<% } %>) MGLStyleValue<<%- propertyType(property, true) %>> *<%- camelizeWithLeadingLowercase(originalPropertyName(property)) %> __attribute__((unavailable("Use <%- camelizeWithLeadingLowercase(property.name) %> instead.")));
+
+<% } -%>
+<% } -%>
+<% } -%>
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLLight.mm b/platform/darwin/src/MGLLight.mm
index 262fad3b07..c83ef127a6 100644
--- a/platform/darwin/src/MGLLight.mm
+++ b/platform/darwin/src/MGLLight.mm
@@ -1,3 +1,7 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+// test
+
#import "MGLLight.h"
#import "MGLTypes.h"
@@ -9,7 +13,7 @@
#import <mbgl/style/types.hpp>
namespace mbgl {
-
+
MBGL_DEFINE_ENUM(MGLLightAnchor, {
{ MGLLightAnchorMap, "map" },
{ MGLLightAnchorViewport, "viewport" },
@@ -47,11 +51,9 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
} else {
anchorStyleValue = MGLStyleValueTransformer<mbgl::style::LightAnchorType, NSValue *, mbgl::style::LightAnchorType, MGLLightAnchor>().toEnumStyleValue(anchor);
}
-
+
_anchor = anchorStyleValue;
-
- _anchorTransition = MGLTransitionFromOptions(mbglLight->getAnchorTransition());
-
+
auto positionValue = mbglLight->getPosition();
if (positionValue.isUndefined()) {
_position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(mbglLight->getDefaultPosition());
@@ -59,8 +61,8 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
_position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toStyleValue(positionValue);
}
- _positionTransiton = MGLTransitionFromOptions(mbglLight->getPositionTransition());
-
+ _positionTransition = MGLTransitionFromOptions(mbglLight->getPositionTransition());
+
auto colorValue = mbglLight->getColor();
if (colorValue.isUndefined()) {
_color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(mbglLight->getDefaultColor());
@@ -68,8 +70,8 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
_color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toStyleValue(colorValue);
}
- _colorTransiton = MGLTransitionFromOptions(mbglLight->getColorTransition());
-
+ _colorTransition = MGLTransitionFromOptions(mbglLight->getColorTransition());
+
auto intensityValue = mbglLight->getIntensity();
if (intensityValue.isUndefined()) {
_intensity = MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(mbglLight->getDefaultIntensity());
@@ -78,6 +80,7 @@ NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition
}
_intensityTransition = MGLTransitionFromOptions(mbglLight->getIntensityTransition());
+
}
return self;
@@ -86,26 +89,24 @@ 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);
mbglLight.setAnchor(anchor);
-
- mbglLight.setAnchorTransition(MGLOptionsFromTransition(self.anchorTransition));
-
+
auto position = MGLStyleValueTransformer<mbgl::style::Position, NSValue *>().toInterpolatablePropertyValue(self.position);
mbglLight.setPosition(position);
-
- mbglLight.setPositionTransition(MGLOptionsFromTransition(self.positionTransiton));
-
+
+ mbglLight.setPositionTransition(MGLOptionsFromTransition(self.positionTransition));
+
auto color = MGLStyleValueTransformer<mbgl::Color, MGLColor *>().toInterpolatablePropertyValue(self.color);
mbglLight.setColor(color);
-
- mbglLight.setColorTransition(MGLOptionsFromTransition(self.colorTransiton));
-
+
+ mbglLight.setColorTransition(MGLOptionsFromTransition(self.colorTransition));
+
auto intensity = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(self.intensity);
mbglLight.setIntensity(intensity);
-
+
mbglLight.setIntensityTransition(MGLOptionsFromTransition(self.intensityTransition));
+
return mbglLight;
}
diff --git a/platform/darwin/src/MGLLight.mm.ejs b/platform/darwin/src/MGLLight.mm.ejs
new file mode 100644
index 0000000000..0d0da124c8
--- /dev/null
+++ b/platform/darwin/src/MGLLight.mm.ejs
@@ -0,0 +1,114 @@
+<%
+ const properties = locals.properties;
+-%>
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+// test
+
+#import "MGLLight.h"
+
+#import "MGLTypes.h"
+#import "NSDate+MGLAdditions.h"
+#import "MGLStyleValue_Private.h"
+#import "NSValue+MGLAdditions.h"
+
+#import <mbgl/style/light.hpp>
+#import <mbgl/style/types.hpp>
+
+namespace mbgl {
+
+ MBGL_DEFINE_ENUM(MGLLightAnchor, {
+<% for (const property of properties) { -%>
+<% if (property.type == "enum") { -%>
+<% for (const value in property.values) { -%>
+ { MGLLightAnchor<%- camelize(value) %>, "<%- value %>" },
+<% } -%>
+<% } -%>
+<% } -%>
+ });
+
+}
+
+NS_INLINE MGLTransition MGLTransitionFromOptions(const mbgl::style::TransitionOptions& options) {
+ MGLTransition transition;
+ transition.duration = MGLTimeIntervalFromDuration(options.duration.value_or(mbgl::Duration::zero()));
+ transition.delay = MGLTimeIntervalFromDuration(options.delay.value_or(mbgl::Duration::zero()));
+
+ return transition;
+}
+
+NS_INLINE mbgl::style::TransitionOptions MGLOptionsFromTransition(MGLTransition transition) {
+ mbgl::style::TransitionOptions options { { MGLDurationFromTimeInterval(transition.duration) }, { MGLDurationFromTimeInterval(transition.delay) } };
+ return options;
+}
+
+@interface MGLLight()
+
+@end
+
+@implementation MGLLight
+
+- (instancetype)initWithMBGLLight:(const mbgl::style::Light *)mbglLight
+{
+ if (self = [super init]) {
+<% if (properties.length) { -%>
+<% for (const property of properties) { -%>
+<% if (property.type == "enum") { -%>
+ auto <%- camelizeWithLeadingLowercase(property.name) -%> = mbglLight->get<%- camelize(property.name) -%>();
+ MGLStyleValue<NSValue *> *<%- camelizeWithLeadingLowercase(property.name) -%>StyleValue;
+ 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) -%>);
+ } 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) -%> = <%- camelizeWithLeadingLowercase(property.name) -%>StyleValue;
+
+<% if (property.transition) { -%>
+ _<%- camelizeWithLeadingLowercase(property.name) -%>Transition = MGLTransitionFromOptions(mbglLight->get<%- camelize(property.name) -%>Transition());
+
+<% } -%>
+<% } 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) -%>());
+ } else {
+ _<%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toStyleValue(<%- camelizeWithLeadingLowercase(property.name) -%>Value);
+ }
+<% if (property.transition) { -%>
+ _<%- camelizeWithLeadingLowercase(property.name) -%>Transition = MGLTransitionFromOptions(mbglLight->get<%- camelize(property.name) -%>Transition());
+<% } -%>
+<% } -%>
+<% } -%>
+<% } -%>
+ }
+
+ return self;
+}
+
+- (mbgl::style::Light)mbglLight
+{
+ mbgl::style::Light mbglLight;
+<% 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) -%>);
+ mbglLight.set<%- camelize(property.name) -%>(<%- camelizeWithLeadingLowercase(property.name) -%>);
+
+<% } else {-%>
+ auto <%- camelizeWithLeadingLowercase(property.name) -%> = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toInterpolatablePropertyValue(self.<%- camelizeWithLeadingLowercase(property.name) -%>);
+ mbglLight.set<%- camelize(property.name) -%>(<%- camelizeWithLeadingLowercase(property.name) -%>);
+
+<% } -%>
+<% if (property.transition) { -%>
+ mbglLight.set<%- camelize(property.name) -%>Transition(MGLOptionsFromTransition(self.<%- camelizeWithLeadingLowercase(property.name) -%>Transition));
+
+<% } -%>
+<% } -%>
+<% } -%>
+
+ return mbglLight;
+}
+
+@end
diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h
index 4a96b11abf..46025ddbf0 100644
--- a/platform/darwin/src/MGLLineStyleLayer.h
+++ b/platform/darwin/src/MGLLineStyleLayer.h
@@ -149,8 +149,18 @@ MGL_EXPORT
You can set this property to an instance of:
* `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * `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 *> *lineJoin;
diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm
index 9be1667722..5b2652cdeb 100644
--- a/platform/darwin/src/MGLLineStyleLayer.mm
+++ b/platform/darwin/src/MGLLineStyleLayer.mm
@@ -109,7 +109,7 @@ namespace mbgl {
- (void)setLineJoin:(MGLStyleValue<NSValue *> *)lineJoin {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toEnumPropertyValue(lineJoin);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenPropertyValue(lineJoin);
self.rawLayer->setLineJoin(mbglValue);
}
@@ -118,9 +118,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getLineJoin();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toEnumStyleValue(self.rawLayer->getDefaultLineJoin());
+ return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenStyleValue(self.rawLayer->getDefaultLineJoin());
}
- return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::LineJoinType, NSValue *, mbgl::style::LineJoinType, MGLLineJoin>().toDataDrivenStyleValue(propertyValue);
}
- (void)setLineMiterLimit:(MGLStyleValue<NSNumber *> *)lineMiterLimit {
diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h
new file mode 100644
index 0000000000..541bc68b93
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter.h
@@ -0,0 +1,206 @@
+#import <Foundation/Foundation.h>
+#import "MGLTypes.h"
+#import "MGLGeometry.h"
+#import "MGLMapCamera.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The options to use when creating images with the `MGLMapSnapshotter`.
+ */
+MGL_EXPORT
+@interface MGLMapSnapshotOptions : NSObject
+
+/**
+ Creates a set of options with the minimum required information.
+
+ @param styleURL URL of the map style to snapshot. The URL may be a full HTTP or HTTPS URL,
+ a Mapbox URL indicating the style’s map ID (`mapbox://styles/{user}/{style`}), or a path
+ to a local file relative to the application’s resource path. Specify `nil` for the default style.
+ @param size The image size.
+ */
+- (instancetype)initWithStyleURL:(nullable NSURL *)styleURL camera:(MGLMapCamera *)camera size:(CGSize)size;
+
+#pragma mark - Configuring the Map
+
+/**
+ URL of the map style to snapshot.
+ */
+@property (nonatomic, readonly) NSURL *styleURL;
+
+/**
+ The zoom level.
+
+ The default zoom level is 0. If this property is non-zero and the camera property
+ is non-nil, the camera’s altitude is ignored in favor of this property’s value.
+ */
+@property (nonatomic) double zoomLevel;
+
+/**
+ A camera representing the viewport visible in the snapshot.
+
+ If this property is non-nil and the `coordinateBounds` property is set to a non-empty
+ coordinate bounds, the camera’s center coordinate and altitude are ignored in favor
+ of the `coordinateBounds` property.
+ */
+@property (nonatomic) MGLMapCamera *camera;
+
+/**
+ The coordinate rectangle that encompasses the bounds to capture.
+
+ If this property is non-empty and the camera property is non-nil, the camera’s
+ center coordinate and altitude are ignored in favor of this property’s value.
+ */
+@property (nonatomic) MGLCoordinateBounds coordinateBounds;
+
+#pragma mark - Configuring the Image
+
+/**
+ The size of the output image, measured in points.
+
+ */
+@property (nonatomic, readonly) CGSize size;
+
+/**
+ The scale of the output image. Defaults to the main screen scale.
+
+ The minimum scale is 1.
+ */
+@property (nonatomic) CGFloat scale;
+
+@end
+
+/**
+ An image generated by a snapshotter object.
+ */
+MGL_EXPORT
+@interface MGLMapSnapshot : NSObject
+
+#if TARGET_OS_IPHONE
+/**
+ Converts the specified map coordinate to a point in the coordinate space of the image.
+ */
+- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;
+
+/**
+ The image of the map’s content.
+ */
+@property(nonatomic, readonly) UIImage *image;
+#else
+/**
+ Converts the specified map coordinate to a point in the coordinate space of the image.
+ */
+- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;
+
+/**
+ The image of the map’s content.
+ */
+@property(nonatomic, readonly) NSImage *image;
+#endif
+
+@end
+
+/**
+ A block to processes the result or error of a snapshot request.
+
+ @param snapshot The `MGLMapSnapshot` that was generated or `nil` if an error occurred.
+ @param error The error that occured or `nil` when successful.
+ */
+typedef void (^MGLMapSnapshotCompletionHandler)(MGLMapSnapshot* _Nullable snapshot, NSError* _Nullable error);
+
+/**
+ An immutable utility object for capturing map-based images.
+
+ ### Example
+
+ ```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))
+ options.zoomLevel = 10
+
+ let snapshotter = MGLMapSnapshotter(options: options)
+ snapshotter.start { (snapshot, error) in
+ if error != nil {
+ // error handler
+ } else {
+ // image handler
+ }
+ }
+ ```
+ */
+MGL_EXPORT
+@interface MGLMapSnapshotter : NSObject
+
+- (instancetype)initWithOptions:(MGLMapSnapshotOptions*)options;
+
+/**
+ Starts the snapshot creation and executes the specified block with the result.
+
+ @param completionHandler The block to handle the result in.
+ */
+- (void)startWithCompletionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
+
+/**
+ Starts the snapshot creation and executes the specified block with the result on the specified queue.
+
+ @param queue The queue to handle the result on.
+ @param completionHandler The block to handle the result in.
+ */
+- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completionHandler;
+
+/**
+ Cancels the snapshot creation request, if any.
+
+ Once you call this method, you cannot resume the snapshot. In order to obtain the
+ snapshot, create a new `MGLMapSnapshotter` object.
+ */
+- (void)cancel;
+
+/**
+ The zoom level.
+
+ The default zoom level is 0. If this property is non-zero and the camera property
+ is non-nil, the camera’s altitude is ignored in favor of this property’s value.
+ */
+@property (nonatomic) double zoomLevel;
+
+/**
+ A camera representing the viewport visible in the snapshot.
+
+ If this property is non-nil and the `coordinateBounds` property is set to a non-empty
+ coordinate bounds, the camera’s center coordinate and altitude are ignored in favor
+ of the `coordinateBounds` property.
+ */
+@property (nonatomic) MGLMapCamera *camera;
+
+/**
+ The coordinate rectangle that encompasses the bounds to capture.
+
+ If this property is non-empty and the camera property is non-nil, the camera’s
+ center coordinate and altitude are ignored in favor of this property’s value.
+ */
+@property (nonatomic) MGLCoordinateBounds coordinateBounds;
+
+/**
+ URL of the map style to snapshot.
+
+ The URL may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style’s
+ map ID (`mapbox://styles/{user}/{style`}), or a path to a local file relative
+ to the application’s resource path. Specify `nil` for the default style.
+ */
+@property (nonatomic, nullable) NSURL *styleURL;
+
+/**
+ The size of the output image, measured in points.
+ */
+@property (nonatomic) CGSize size;
+
+/**
+ Indicates whether a snapshot is currently being generated.
+ */
+@property (nonatomic, readonly, getter=isLoading) BOOL loading;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm
new file mode 100644
index 0000000000..346666cf80
--- /dev/null
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -0,0 +1,421 @@
+#import "MGLMapSnapshotter.h"
+
+#import <mbgl/actor/actor.hpp>
+#import <mbgl/actor/scheduler.hpp>
+#import <mbgl/util/geo.hpp>
+#import <mbgl/map/map_snapshotter.hpp>
+#import <mbgl/map/camera.hpp>
+#import <mbgl/storage/default_file_source.hpp>
+#import <mbgl/util/default_thread_pool.hpp>
+#import <mbgl/util/string.hpp>
+#import <mbgl/util/shared_thread_pool.hpp>
+
+#import "MGLOfflineStorage_Private.h"
+#import "MGLGeometry_Private.h"
+#import "NSBundle+MGLAdditions.h"
+#import "MGLStyle.h"
+#import "MGLAttributionInfo_Private.h"
+
+#if TARGET_OS_IPHONE
+#import "UIImage+MGLAdditions.h"
+#else
+#import "NSImage+MGLAdditions.h"
+#import <CoreGraphics/CoreGraphics.h>
+#import <QuartzCore/QuartzCore.h>
+#endif
+
+const CGPoint MGLLogoImagePosition = CGPointMake(8, 8);
+const CGFloat MGLSnapshotterMinimumPixelSize = 64;
+
+@implementation MGLMapSnapshotOptions
+
+- (instancetype _Nonnull)initWithStyleURL:(nullable NSURL *)styleURL camera:(MGLMapCamera *)camera size:(CGSize) size
+{
+ self = [super init];
+ if (self) {
+ if ( !styleURL)
+ {
+ styleURL = [MGLStyle streetsStyleURLWithVersion:MGLStyleDefaultVersion];
+ }
+ _styleURL = styleURL;
+ _size = size;
+ _camera = camera;
+#if TARGET_OS_IPHONE
+ _scale = [UIScreen mainScreen].scale;
+#else
+ _scale = [NSScreen mainScreen].backingScaleFactor;
+#endif
+
+ }
+ return self;
+}
+
+@end
+
+@interface MGLMapSnapshot()
+- (instancetype)initWithImage:(nullable MGLImage *)image scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn;
+
+@property (nonatomic) CGFloat scale;
+@end
+
+@implementation MGLMapSnapshot {
+ mbgl::MapSnapshotter::PointForFn _pointForFn;
+}
+- (instancetype)initWithImage:(nullable MGLImage *)image scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn
+{
+ self = [super init];
+ if (self) {
+ _pointForFn = std::move(pointForFn);
+ _scale = scale;
+ _image = image;
+ }
+ return self;
+}
+
+- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate
+{
+ mbgl::ScreenCoordinate sc = _pointForFn(MGLLatLngFromLocationCoordinate2D(coordinate));
+ return CGPointMake(sc.x * self.scale, sc.y * self.scale);
+}
+@end
+
+@interface MGLMapSnapshotter()
+@property (nonatomic) MGLMapSnapshotOptions *options;
+@end
+
+@implementation MGLMapSnapshotter {
+
+ std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool;
+ std::unique_ptr<mbgl::MapSnapshotter> _mbglMapSnapshotter;
+ std::unique_ptr<mbgl::Actor<mbgl::MapSnapshotter::Callback>> _snapshotCallback;
+}
+
+- (instancetype)initWithOptions:(MGLMapSnapshotOptions *)options
+{
+ self = [super init];
+ if (self) {
+ _options = options;
+ _loading = false;
+
+ mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
+ _mbglThreadPool = mbgl::sharedThreadPool();
+
+ std::string styleURL = std::string([options.styleURL.absoluteString UTF8String]);
+
+ // Size; taking into account the minimum texture size for OpenGL ES
+ // For non retina screens the ratio is 1:1 MGLSnapshotterMinimumPixelSize
+ mbgl::Size size = {
+ static_cast<uint32_t>(MAX(options.size.width, MGLSnapshotterMinimumPixelSize)),
+ static_cast<uint32_t>(MAX(options.size.height, MGLSnapshotterMinimumPixelSize))
+ };
+
+ float pixelRatio = MAX(options.scale, 1);
+
+ // Camera options
+ mbgl::CameraOptions cameraOptions;
+ if (CLLocationCoordinate2DIsValid(options.camera.centerCoordinate)) {
+ cameraOptions.center = MGLLatLngFromLocationCoordinate2D(options.camera.centerCoordinate);
+ }
+ cameraOptions.angle = MAX(0, options.camera.heading) * mbgl::util::DEG2RAD;
+ cameraOptions.zoom = MAX(0, options.zoomLevel);
+ cameraOptions.pitch = MAX(0, options.camera.pitch);
+
+ // Region
+ mbgl::optional<mbgl::LatLngBounds> coordinateBounds;
+ if (!MGLCoordinateBoundsIsEmpty(options.coordinateBounds)) {
+ coordinateBounds = MGLLatLngBoundsFromCoordinateBounds(options.coordinateBounds);
+ }
+
+ // Create the snapshotter
+ _mbglMapSnapshotter = std::make_unique<mbgl::MapSnapshotter>(*mbglFileSource, *_mbglThreadPool, styleURL, size, pixelRatio, cameraOptions, coordinateBounds);
+ }
+ return self;
+}
+
+- (void)startWithCompletionHandler:(MGLMapSnapshotCompletionHandler)completion
+{
+ [self startWithQueue:dispatch_get_main_queue() completionHandler:completion];
+}
+
+- (void)startWithQueue:(dispatch_queue_t)queue completionHandler:(MGLMapSnapshotCompletionHandler)completion
+{
+ if ([self isLoading]) {
+ [NSException raise:NSInternalInconsistencyException
+ format:@"Already started this snapshotter."];
+ }
+
+ _loading = true;
+
+ dispatch_async(queue, ^{
+ _snapshotCallback = std::make_unique<mbgl::Actor<mbgl::MapSnapshotter::Callback>>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn) {
+ _loading = false;
+
+ NSMutableArray *infos = [NSMutableArray array];
+
+#if TARGET_OS_IPHONE
+ CGFloat fontSize = [UIFont smallSystemFontSize];
+ UIColor *attributeFontColor = [UIColor blackColor];
+#else
+ CGFloat fontSize = [NSFont systemFontSizeForControlSize:NSMiniControlSize];
+ NSColor *attributeFontColor = [NSColor blackColor];
+#endif
+ for (auto attribution = attributions.begin(); attribution != attributions.end(); ++attribution) {
+ NSString *attributionHTMLString = @(attribution->c_str());
+ NSArray *tileSetInfos = [MGLAttributionInfo attributionInfosFromHTMLString:attributionHTMLString
+ fontSize:fontSize
+ linkColor:attributeFontColor];
+ [infos growArrayByAddingAttributionInfosFromArray:tileSetInfos];
+ }
+
+ CGSize attributionBackgroundSize = CGSizeMake(10, 0);
+ for (MGLAttributionInfo *info in infos) {
+ if (info.isFeedbackLink) {
+ continue;
+ }
+ attributionBackgroundSize.width += [info.title size].width + 10;
+ attributionBackgroundSize.height = MAX([info.title size].height, attributionBackgroundSize.height);
+ }
+
+ if (mbglError) {
+ NSString *description = @(mbgl::util::toString(mbglError).c_str());
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description};
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeSnapshotFailed userInfo:userInfo];
+
+ // Dispatch result to origin queue
+ dispatch_async(queue, ^{
+ completion(nil, error);
+ });
+ } else {
+#if TARGET_OS_IPHONE
+ MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image) scale:self.options.scale];
+#else
+ MGLImage *mglImage = [[MGLImage alloc] initWithMGLPremultipliedImage:std::move(image)];
+ mglImage.size = NSMakeSize(mglImage.size.width / self.options.scale,
+ mglImage.size.height / self.options.scale);
+#endif
+
+ // Process image watermark in a work queue
+ dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ dispatch_async(workQueue, ^{
+#if TARGET_OS_IPHONE
+ UIImage *logoImage = [UIImage imageNamed:@"mapbox" inBundle:[NSBundle mgl_frameworkBundle] compatibleWithTraitCollection:nil];
+
+ CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, mglImage.size.height - (MGLLogoImagePosition.y + logoImage.size.height), logoImage.size.width, logoImage.size.height);
+ CGRect attributionBackgroundFrame = CGRectMake(mglImage.size.width - 10 - attributionBackgroundSize.width,
+ logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2) + 1,
+ attributionBackgroundSize.width,
+ attributionBackgroundSize.height);
+ CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
+ attributionBackgroundFrame.origin.y - 1);
+
+ CGRect cropRect = CGRectMake(attributionBackgroundFrame.origin.x * mglImage.scale,
+ attributionBackgroundFrame.origin.y * mglImage.scale,
+ attributionBackgroundSize.width * mglImage.scale,
+ attributionBackgroundSize.height * mglImage.scale);
+
+
+ UIGraphicsBeginImageContextWithOptions(mglImage.size, NO, self.options.scale);
+
+ [mglImage drawInRect:CGRectMake(0, 0, mglImage.size.width, mglImage.size.height)];
+
+ [logoImage drawInRect:logoImageRect];
+
+ UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext();
+ CGImageRef attributionImageRef = CGImageCreateWithImageInRect([currentImage CGImage], cropRect);
+ UIImage *attributionImage = [UIImage imageWithCGImage:attributionImageRef];
+ CGImageRelease(attributionImageRef);
+
+ CIImage *ciAttributionImage = [[CIImage alloc] initWithCGImage:attributionImage.CGImage];
+
+ UIImage *blurredAttributionBackground = [self blurredAttributionBackground:ciAttributionImage];
+
+ [blurredAttributionBackground drawInRect:attributionBackgroundFrame];
+
+ [self drawAttributionText:infos origin:attributionTextPosition];
+
+ UIImage *compositedImage = UIGraphicsGetImageFromCurrentImageContext();
+
+ UIGraphicsEndImageContext();
+#else
+ NSImage *logoImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mgl_frameworkBundle] pathForResource:@"mapbox" ofType:@"pdf"]];
+ NSImage *sourceImage = mglImage;
+
+ NSSize targetSize = NSMakeSize(self.options.size.width, self.options.size.height);
+ NSRect targetFrame = NSMakeRect(0, 0, targetSize.width, targetSize.height);
+ CGRect logoImageRect = CGRectMake(MGLLogoImagePosition.x, MGLLogoImagePosition.y, logoImage.size.width, logoImage.size.height);
+ CGRect attributionBackgroundFrame = CGRectMake(targetFrame.size.width - 10 - attributionBackgroundSize.width,
+ MGLLogoImagePosition.y + 1,
+ attributionBackgroundSize.width,
+ attributionBackgroundSize.height);
+ CGPoint attributionTextPosition = CGPointMake(attributionBackgroundFrame.origin.x + 10,
+ logoImageRect.origin.y + (logoImageRect.size.height / 2) - (attributionBackgroundSize.height / 2));
+
+
+ NSImage *compositedImage = nil;
+ NSImageRep *sourceImageRep = [sourceImage bestRepresentationForRect:targetFrame
+ context:nil
+ hints:nil];
+ compositedImage = [[NSImage alloc] initWithSize:targetSize];
+
+ [compositedImage lockFocus];
+
+ [sourceImageRep drawInRect: targetFrame];
+
+ [logoImage drawInRect:logoImageRect];
+
+ NSBitmapImageRep *attributionBackground = [[NSBitmapImageRep alloc] initWithFocusedViewRect:attributionBackgroundFrame];
+
+ CIImage *attributionBackgroundImage = [[CIImage alloc] initWithCGImage:[attributionBackground CGImage]];
+
+ NSImage *blurredAttributionBackground = [self blurredAttributionBackground:attributionBackgroundImage];
+
+ [blurredAttributionBackground drawInRect:attributionBackgroundFrame];
+
+ [self drawAttributionText:infos origin:attributionTextPosition];
+
+ [compositedImage unlockFocus];
+
+
+#endif
+
+ // Dispatch result to origin queue
+ dispatch_async(queue, ^{
+ MGLMapSnapshot* snapshot = [[MGLMapSnapshot alloc] initWithImage:compositedImage scale:self.options.scale pointForFn:pointForFn];
+ completion(snapshot, nil);
+ });
+ });
+ }
+ });
+ _mbglMapSnapshotter->snapshot(_snapshotCallback->self());
+ });
+}
+
+- (void)drawAttributionText:(NSArray *)attributionInfo origin:(CGPoint)origin
+{
+ for (MGLAttributionInfo *info in attributionInfo) {
+ if (info.isFeedbackLink) {
+ continue;
+ }
+ [info.title drawAtPoint:origin];
+
+ origin.x += [info.title size].width + 10;
+ }
+}
+
+- (MGLImage *)blurredAttributionBackground:(CIImage *)backgroundImage
+{
+ CGAffineTransform transform = CGAffineTransformIdentity;
+ CIFilter *clamp = [CIFilter filterWithName:@"CIAffineClamp"];
+ [clamp setValue:backgroundImage forKey:kCIInputImageKey];
+ [clamp setValue:[NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)] forKey:@"inputTransform"];
+
+ CIFilter *attributionBlurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
+ [attributionBlurFilter setValue:[clamp outputImage] forKey:kCIInputImageKey];
+ [attributionBlurFilter setValue:@10 forKey:kCIInputRadiusKey];
+
+ CIFilter *attributionColorFilter = [CIFilter filterWithName:@"CIColorControls"];
+ [attributionColorFilter setValue:[attributionBlurFilter outputImage] forKey:kCIInputImageKey];
+ [attributionColorFilter setValue:@(0.1) forKey:kCIInputBrightnessKey];
+
+ CIImage *blurredImage = attributionColorFilter.outputImage;
+
+ CIContext *ctx = [CIContext contextWithOptions:nil];
+ CGImageRef cgimg = [ctx createCGImage:blurredImage fromRect:[backgroundImage extent]];
+ MGLImage *image;
+
+#if TARGET_OS_IPHONE
+
+ image = [UIImage imageWithCGImage:cgimg];
+#else
+
+ image = [[NSImage alloc] initWithCGImage:cgimg size:[backgroundImage extent].size];
+#endif
+
+ CGImageRelease(cgimg);
+ return image;
+}
+
+- (void)cancel
+{
+ _snapshotCallback.reset();
+ _mbglMapSnapshotter.reset();
+}
+
+- (NSURL *)styleURL
+{
+ NSString *styleURLString = @(_mbglMapSnapshotter->getStyleURL().c_str());
+ return styleURLString.length ? [NSURL URLWithString:styleURLString] : nil;
+}
+
+- (void)setStyleURL:(NSURL *)url
+{
+ _mbglMapSnapshotter->setStyleURL(std::string([url.absoluteString UTF8String]));
+}
+
+- (CGSize)size
+{
+ mbgl::Size size = _mbglMapSnapshotter->getSize();
+ return CGSizeMake(size.width, size.height);
+}
+
+- (void)setSize:(CGSize)size
+{
+ _mbglMapSnapshotter->setSize({
+ static_cast<uint32_t>(MAX(size.width, MGLSnapshotterMinimumPixelSize)),
+ static_cast<uint32_t>(MAX(size.height, MGLSnapshotterMinimumPixelSize))
+ });
+}
+
+- (MGLMapCamera *)camera
+{
+ mbgl::CameraOptions cameraOptions = _mbglMapSnapshotter->getCameraOptions();
+ CGFloat pitch = *cameraOptions.pitch;
+ CLLocationDirection heading = mbgl::util::wrap(*cameraOptions.angle, 0., 360.);
+ CLLocationDistance distance = MGLAltitudeForZoomLevel(*cameraOptions.zoom, pitch, cameraOptions.center->latitude(), [self size]);
+ return [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(*cameraOptions.center)
+ fromDistance:distance
+ pitch:pitch
+ heading:heading];
+}
+
+- (void)setCamera:(MGLMapCamera *)camera
+{
+ mbgl::CameraOptions cameraOptions;
+ CLLocationCoordinate2D center;
+ if (CLLocationCoordinate2DIsValid(camera.centerCoordinate)) {
+ cameraOptions.center = MGLLatLngFromLocationCoordinate2D(camera.centerCoordinate);
+ center = camera.centerCoordinate;
+ } else {
+ // Center is optional, but always set.
+ center = MGLLocationCoordinate2DFromLatLng(*_mbglMapSnapshotter->getCameraOptions().center);
+ }
+
+ cameraOptions.angle = MAX(0, camera.heading) * mbgl::util::DEG2RAD;
+ cameraOptions.zoom = MAX(0, MGLZoomLevelForAltitude(camera.altitude, camera.pitch, center.latitude, [self size]));
+ cameraOptions.pitch = MAX(0, camera.pitch);
+}
+
+- (double)zoomLevel
+{
+ mbgl::CameraOptions cameraOptions = _mbglMapSnapshotter->getCameraOptions();
+ return MGLAltitudeForZoomLevel(*cameraOptions.zoom, *cameraOptions.pitch, cameraOptions.center->latitude(), [self size]);
+}
+
+- (void)setZoomLevel:(double)zoomLevel
+{
+ mbgl::CameraOptions cameraOptions = _mbglMapSnapshotter->getCameraOptions();
+ cameraOptions.zoom = zoomLevel;
+ _mbglMapSnapshotter->setCameraOptions(cameraOptions);
+}
+
+- (MGLCoordinateBounds)coordinateBounds
+{
+ return MGLCoordinateBoundsFromLatLngBounds(_mbglMapSnapshotter->getRegion());
+}
+
+- (void)setCoordinateBounds:(MGLCoordinateBounds)coordinateBounds
+{
+ _mbglMapSnapshotter->setRegion(MGLLatLngBoundsFromCoordinateBounds(coordinateBounds));
+}
+
+@end
diff --git a/platform/darwin/src/MGLMultiPoint.h b/platform/darwin/src/MGLMultiPoint.h
index 429bbdb22d..ee9eb530a4 100644
--- a/platform/darwin/src/MGLMultiPoint.h
+++ b/platform/darwin/src/MGLMultiPoint.h
@@ -10,8 +10,9 @@ NS_ASSUME_NONNULL_BEGIN
The `MGLMultiPoint` class is an abstract superclass used to define shapes
composed of multiple vertices.
- You do not create instances of this class directly. Instead, you create
- instances of the `MGLPolyline` or `MGLPolygon` classes. However, you can use
+ Create instances of `MGLPolyline` or `MGLPolygon` in order to use
+ properties of `MGLMultiPoint`. Do not create instances of `MGLMultiPoint`
+ directly and do not create your own subclasses of this class. You can use
the method and properties of this class to access information about the
vertices of the line or polygon.
diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm
index ef46bbb0fe..240dad9614 100644
--- a/platform/darwin/src/MGLMultiPoint.mm
+++ b/platform/darwin/src/MGLMultiPoint.mm
@@ -182,7 +182,7 @@
- (mbgl::Annotation)annotationObjectWithDelegate:(__unused id <MGLMultiPointDelegate>)delegate
{
NSAssert(NO, @"Cannot add an annotation from an instance of %@", NSStringFromClass([self class]));
- return mbgl::SymbolAnnotation({mbgl::Point<double>()});
+ return mbgl::SymbolAnnotation(mbgl::Point<double>());
}
- (NSString *)description
diff --git a/platform/darwin/src/MGLNetworkConfiguration.m b/platform/darwin/src/MGLNetworkConfiguration.m
index 82d333dc99..4cfa405a1a 100644
--- a/platform/darwin/src/MGLNetworkConfiguration.m
+++ b/platform/darwin/src/MGLNetworkConfiguration.m
@@ -13,7 +13,7 @@
+ (instancetype)sharedManager {
static dispatch_once_t onceToken;
static MGLNetworkConfiguration *_sharedManager;
- void (^setupBlock)() = ^{
+ void (^setupBlock)(void) = ^{
dispatch_once(&onceToken, ^{
_sharedManager = [[self alloc] init];
});
diff --git a/platform/darwin/src/MGLOfflinePack.h b/platform/darwin/src/MGLOfflinePack.h
index 0b2db35b1a..dfc47bf1c8 100644
--- a/platform/darwin/src/MGLOfflinePack.h
+++ b/platform/darwin/src/MGLOfflinePack.h
@@ -54,7 +54,7 @@ typedef NS_ENUM (NSInteger, MGLOfflinePackState) {
A structure containing information about an offline pack’s current download
progress.
*/
-typedef struct MGLOfflinePackProgress {
+typedef struct __attribute__((objc_boxable)) MGLOfflinePackProgress {
/**
The number of resources, including tiles, that have been completely
downloaded and are ready to use offline.
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index 2ddfa649e9..7085aa58e5 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -11,6 +11,7 @@
#import "NSValue+MGLAdditions.h"
#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/string.hpp>
@@ -81,7 +82,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
- (void)setDelegate:(id<MGLOfflineStorageDelegate>)newValue {
_delegate = newValue;
if ([self.delegate respondsToSelector:@selector(offlineStorage:URLForResourceOfKind:withURL:)]) {
- _mbglResourceTransform = std::make_unique<mbgl::Actor<mbgl::ResourceTransform>>(*mbgl::util::RunLoop::Get(), [offlineStorage = self](auto kind_, const std::string&& url_) -> std::string {
+ _mbglResourceTransform = std::make_unique<mbgl::Actor<mbgl::ResourceTransform>>(*mbgl::Scheduler::GetCurrent(), [offlineStorage = self](auto kind_, const std::string&& url_) -> std::string {
NSURL* url =
[NSURL URLWithString:[[NSString alloc] initWithBytes:url_.data()
length:url_.length()
diff --git a/platform/darwin/src/MGLPolygon+MGLAdditions.h b/platform/darwin/src/MGLPolygon+MGLAdditions.h
deleted file mode 100644
index f409fb96ca..0000000000
--- a/platform/darwin/src/MGLPolygon+MGLAdditions.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-@interface MGLPolygon (MGLAdditions)
-
-- (NS_ARRAY_OF(id) *)mgl_coordinates;
-
-@end
diff --git a/platform/darwin/src/MGLPolygon+MGLAdditions.m b/platform/darwin/src/MGLPolygon+MGLAdditions.m
deleted file mode 100644
index 3e76a37157..0000000000
--- a/platform/darwin/src/MGLPolygon+MGLAdditions.m
+++ /dev/null
@@ -1,27 +0,0 @@
-#import "MGLPolygon+MGLAdditions.h"
-
-@implementation MGLPolygon (MGLAdditions)
-
-- (NS_ARRAY_OF(id) *)mgl_coordinates {
- NSMutableArray *coordinates = [NSMutableArray array];
-
- NSMutableArray *exteriorRing = [NSMutableArray array];
- for (NSUInteger index = 0; index < self.pointCount; index++) {
- CLLocationCoordinate2D coordinate = self.coordinates[index];
- [exteriorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
- }
- [coordinates addObject:exteriorRing];
-
- for (MGLPolygon *interiorPolygon in self.interiorPolygons) {
- NSMutableArray *interiorRing = [NSMutableArray array];
- for (int index = 0; index < interiorPolygon.pointCount; index++) {
- CLLocationCoordinate2D coordinate = interiorPolygon.coordinates[index];
- [interiorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
- }
- [coordinates addObject:interiorRing];
- }
-
- return [coordinates copy];
-}
-
-@end
diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm
index d966ff13ce..2af768d514 100644
--- a/platform/darwin/src/MGLPolygon.mm
+++ b/platform/darwin/src/MGLPolygon.mm
@@ -1,9 +1,9 @@
-#import "MGLPolygon.h"
+#import "MGLPolygon_Private.h"
#import "MGLMultiPoint_Private.h"
#import "MGLGeometry_Private.h"
-#import "MGLPolygon+MGLAdditions.h"
+#import "MGLFeature.h"
#import <mbgl/util/geojson.hpp>
#import <mapbox/polylabel.hpp>
@@ -102,6 +102,28 @@
@"coordinates": self.mgl_coordinates};
}
+- (NS_ARRAY_OF(id) *)mgl_coordinates {
+ NSMutableArray *coordinates = [NSMutableArray array];
+
+ NSMutableArray *exteriorRing = [NSMutableArray array];
+ for (NSUInteger index = 0; index < self.pointCount; index++) {
+ CLLocationCoordinate2D coordinate = self.coordinates[index];
+ [exteriorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
+ }
+ [coordinates addObject:exteriorRing];
+
+ for (MGLPolygon *interiorPolygon in self.interiorPolygons) {
+ NSMutableArray *interiorRing = [NSMutableArray array];
+ for (int index = 0; index < interiorPolygon.pointCount; index++) {
+ CLLocationCoordinate2D coordinate = interiorPolygon.coordinates[index];
+ [interiorRing addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
+ }
+ [coordinates addObject:interiorRing];
+ }
+
+ return [coordinates copy];
+}
+
@end
@interface MGLMultiPolygon ()
@@ -200,4 +222,14 @@
@"coordinates": coordinates};
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
+ self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle,
+ (unsigned long)self.polygons.count,
+ MGLStringFromCoordinateBounds(self.overlayBounds)];
+}
+
@end
diff --git a/platform/darwin/src/MGLPolygon_Private.h b/platform/darwin/src/MGLPolygon_Private.h
new file mode 100644
index 0000000000..75afcd61f6
--- /dev/null
+++ b/platform/darwin/src/MGLPolygon_Private.h
@@ -0,0 +1,11 @@
+#import "MGLPolygon.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLPolygon (Private)
+
+- (NS_ARRAY_OF(id) *)mgl_coordinates;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLPolyline+MGLAdditions.h b/platform/darwin/src/MGLPolyline+MGLAdditions.h
deleted file mode 100644
index 4cdbbf17f9..0000000000
--- a/platform/darwin/src/MGLPolyline+MGLAdditions.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-@interface MGLPolyline (MGLAdditions)
-
-- (NS_ARRAY_OF(id) *)mgl_coordinates;
-
-@end
diff --git a/platform/darwin/src/MGLPolyline+MGLAdditions.m b/platform/darwin/src/MGLPolyline+MGLAdditions.m
deleted file mode 100644
index d1db2c58a0..0000000000
--- a/platform/darwin/src/MGLPolyline+MGLAdditions.m
+++ /dev/null
@@ -1,14 +0,0 @@
-#import "MGLPolyline+MGLAdditions.h"
-
-@implementation MGLPolyline (MGLAdditions)
-
-- (NS_ARRAY_OF(id) *)mgl_coordinates {
- NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
- for (NSUInteger index = 0; index < self.pointCount; index++) {
- CLLocationCoordinate2D coordinate = self.coordinates[index];
- [coordinates addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
- }
- return [coordinates copy];
-}
-
-@end
diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h
index b3db0fd39f..e46baa91cc 100644
--- a/platform/darwin/src/MGLPolyline.h
+++ b/platform/darwin/src/MGLPolyline.h
@@ -33,8 +33,18 @@ NS_ASSUME_NONNULL_BEGIN
`MGLPolygon` object. To group multiple polylines together in one shape, use an
`MGLMultiPolyline` or `MGLShapeCollection` object.
- To make the polyline straddle the antimeridian, specify some longitudes less
- than −180 degrees or greater than 180 degrees.
+ To make the polyline go across the antimeridian or international date line,
+ specify some longitudes less than −180 degrees or greater than 180 degrees.
+ For example, a polyline that stretches from Tokyo to San Francisco would have
+ coordinates of (35.68476, -220.24257) and (37.78428, -122.41310).
+
+ ```swift
+ let coordinates = [
+ CLLocationCoordinate2D(latitude: 35.68476, longitude: -220.24257),
+ CLLocationCoordinate2D(latitude: 37.78428, longitude: -122.41310)
+ ]
+ let polyline = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count))
+ ```
A polyline is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.1.4">LineString</a>
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index fd75dc2795..e011d09215 100644
--- a/platform/darwin/src/MGLPolyline.mm
+++ b/platform/darwin/src/MGLPolyline.mm
@@ -1,9 +1,9 @@
-#import "MGLPolyline.h"
+#import "MGLPolyline_Private.h"
#import "MGLMultiPoint_Private.h"
#import "MGLGeometry_Private.h"
-#import "MGLPolyline+MGLAdditions.h"
+#import "MGLFeature.h"
#import <mbgl/util/geojson.hpp>
#import <mapbox/polylabel.hpp>
@@ -49,6 +49,15 @@
@"coordinates": self.mgl_coordinates};
}
+- (NS_ARRAY_OF(id) *)mgl_coordinates {
+ NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount];
+ for (NSUInteger index = 0; index < self.pointCount; index++) {
+ CLLocationCoordinate2D coordinate = self.coordinates[index];
+ [coordinates addObject:@[@(coordinate.longitude), @(coordinate.latitude)]];
+ }
+ return [coordinates copy];
+}
+
- (BOOL)isEqual:(id)other {
return self == other || ([other isKindOfClass:[MGLPolyline class]] && [super isEqual:other]);
}
@@ -201,4 +210,14 @@
@"coordinates": coordinates};
}
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"<%@: %p; title = %@, subtitle: = %@, count = %lu; bounds = %@>",
+ NSStringFromClass([self class]), (void *)self,
+ self.title ? [NSString stringWithFormat:@"\"%@\"", self.title] : self.title,
+ self.subtitle ? [NSString stringWithFormat:@"\"%@\"", self.subtitle] : self.subtitle,
+ (unsigned long)self.polylines.count,
+ MGLStringFromCoordinateBounds(self.overlayBounds)];
+}
+
@end
diff --git a/platform/darwin/src/MGLPolyline_Private.h b/platform/darwin/src/MGLPolyline_Private.h
new file mode 100644
index 0000000000..405a0c5bc9
--- /dev/null
+++ b/platform/darwin/src/MGLPolyline_Private.h
@@ -0,0 +1,12 @@
+#import "MGLPolyline.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLPolyline (Private)
+
+- (NS_ARRAY_OF(id) *)mgl_coordinates;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
diff --git a/platform/darwin/src/MGLRendererConfiguration.h b/platform/darwin/src/MGLRendererConfiguration.h
new file mode 100644
index 0000000000..31aad0a742
--- /dev/null
+++ b/platform/darwin/src/MGLRendererConfiguration.h
@@ -0,0 +1,40 @@
+#import <Foundation/Foundation.h>
+#import <mbgl/storage/default_file_source.hpp>
+#import <mbgl/renderer/mode.hpp>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The MGLRendererConfiguration object represents configuration values for the
+ renderer.
+ */
+@interface MGLRendererConfiguration : NSObject
+
+/** Returns an instance of the current renderer configuration. */
++ (instancetype)currentConfiguration;
+
+/** The file source to use. Defaults to `mbgl::DefaultFileSource` */
+@property (nonatomic, readonly) mbgl::DefaultFileSource *fileSource;
+
+/** The GL context mode to use. Defaults to `mbgl::GLContextMode::Unique` */
+@property (nonatomic, readonly) mbgl::GLContextMode contextMode;
+
+/** The scale factor to use.
+
+ Based on the native scale where available, otherwise the standard screen scale. */
+@property (nonatomic, readonly) const float scaleFactor;
+
+/** The cache dir to use. */
+@property (nonatomic, readonly) mbgl::optional<std::string> cacheDir;
+
+/** The name of the font family to use for client-side text rendering.
+
+ Currently only used for CJK glyphs. Changing this at run time is not currently
+ supported. Enable client-side rendering of CJK glyphs by setting
+ `MGLIdeographicFontFamilyName` in your containing app's Info.plist to a value
+ which will be available at run time, e.g. "PingFang". */
+@property (nonatomic, readonly) mbgl::optional<std::string> localFontFamilyName;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLRendererConfiguration.mm b/platform/darwin/src/MGLRendererConfiguration.mm
new file mode 100644
index 0000000000..ae7d7dd9fe
--- /dev/null
+++ b/platform/darwin/src/MGLRendererConfiguration.mm
@@ -0,0 +1,43 @@
+#import "MGLRendererConfiguration.h"
+#import "MGLOfflineStorage_Private.h"
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#else
+#import <AppKit/AppKit.h>
+#endif
+
+
+@implementation MGLRendererConfiguration
+
++ (instancetype)currentConfiguration {
+ return [[self alloc] init];
+}
+
+- (mbgl::DefaultFileSource *)fileSource {
+ return [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
+}
+
+- (mbgl::GLContextMode)contextMode {
+ return mbgl::GLContextMode::Unique;
+}
+
+- (const float)scaleFactor {
+#if TARGET_OS_IPHONE
+ return [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
+#else
+ return [NSScreen mainScreen].backingScaleFactor;
+#endif
+}
+
+- (mbgl::optional<std::string>)cacheDir {
+ return mbgl::optional<std::string>();
+}
+
+- (mbgl::optional<std::string>)localFontFamilyName {
+ NSString *fontFamilyName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLIdeographicFontFamilyName"];
+
+ return fontFamilyName ? std::string([fontFamilyName UTF8String]) : mbgl::optional<std::string>();
+}
+
+@end
diff --git a/platform/darwin/src/MGLRendererFrontend.h b/platform/darwin/src/MGLRendererFrontend.h
new file mode 100644
index 0000000000..76904d008b
--- /dev/null
+++ b/platform/darwin/src/MGLRendererFrontend.h
@@ -0,0 +1,70 @@
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/renderer_frontend.hpp>
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/optional.hpp>
+
+
+/**
+ The RenderFrontend is passed to the Map to facilitate rendering in a platform
+ dependent way.
+ */
+class MGLRenderFrontend : public mbgl::RendererFrontend
+{
+public:
+ MGLRenderFrontend(std::unique_ptr<mbgl::Renderer> renderer_, MGLMapView* nativeView_, mbgl::RendererBackend& mbglBackend_, bool async = false)
+ : renderer(std::move(renderer_))
+ , nativeView(nativeView_)
+ , mbglBackend(mbglBackend_) {
+ if (async) {
+ asyncInvalidate.emplace([&]() {
+ [nativeView setNeedsGLDisplay];
+ });
+ }
+ }
+
+ void reset() override {
+ if (renderer) {
+ renderer.reset();
+ }
+ }
+
+ void update(std::shared_ptr<mbgl::UpdateParameters> updateParameters_) override {
+ updateParameters = std::move(updateParameters_);
+ if (asyncInvalidate) {
+ asyncInvalidate->send();
+ } else {
+ [nativeView setNeedsGLDisplay];
+ }
+ }
+
+ void setObserver(mbgl::RendererObserver& observer) override {
+ if (!renderer) return;
+ renderer->setObserver(&observer);
+ }
+
+ void render() {
+ if (!renderer || !updateParameters) return;
+
+ mbgl::BackendScope guard { mbglBackend, mbgl::BackendScope::ScopeType::Implicit };
+
+ renderer->render(*updateParameters);
+ }
+
+ mbgl::Renderer* getRenderer() {
+ return renderer.get();
+ }
+
+ void onLowMemory() {
+ if (!renderer) return;
+ renderer->onLowMemory();
+ }
+
+private:
+ std::unique_ptr<mbgl::Renderer> renderer;
+ __weak MGLMapView *nativeView = nullptr;
+ mbgl::RendererBackend& mbglBackend;
+ std::shared_ptr<mbgl::UpdateParameters> updateParameters;
+ mbgl::optional<mbgl::util::AsyncTask> asyncInvalidate;
+};
diff --git a/platform/darwin/src/MGLShape.h b/platform/darwin/src/MGLShape.h
index bd8b6152d2..e965710552 100644
--- a/platform/darwin/src/MGLShape.h
+++ b/platform/darwin/src/MGLShape.h
@@ -10,11 +10,11 @@ NS_ASSUME_NONNULL_BEGIN
constitute the content of a map – not only the overlays atop the map, but also
the content that forms the base map.
- You do not create instances of this class directly or create subclasses of this
- class. Instead, you create instances of `MGLPointAnnotation`,
- `MGLPointCollection`, `MGLPolyline`, `MGLMultiPolyline`, `MGLPolygon`,
- `MGLMultiPolygon`, or `MGLShapeCollection`. The shape classes correspond to the
- <a href="https://tools.ietf.org/html/rfc7946#section-3.1">Geometry</a> object
+ 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
types in the GeoJSON standard, but some have nonstandard names for backwards
compatibility.
diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h
index d0097f748e..ca150edac1 100644
--- a/platform/darwin/src/MGLShapeSource.h
+++ b/platform/darwin/src/MGLShapeSource.h
@@ -1,4 +1,4 @@
-#import "MGLSource.h"
+#import "MGLAbstractShapeSource.h"
#import "MGLFoundation.h"
#import "MGLTypes.h"
@@ -14,78 +14,13 @@ NS_ASSUME_NONNULL_BEGIN
typedef NSString *MGLShapeSourceOption NS_STRING_ENUM;
/**
- An `NSNumber` object containing a Boolean enabling or disabling clustering.
- If the `shape` property contains point shapes, setting this option to
- `YES` clusters the points by radius into groups. The default value is `NO`.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-cluster"><code>cluster</code></a>
- source property in the Mapbox Style Specification.
-
- This option only affects point features within a shape source.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClustered;
-
-/**
- An `NSNumber` object containing an integer; specifies the radius of each
- cluster if clustering is enabled. A value of 512 produces a radius equal to
- the width of a tile. The default value is 50.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
-
-/**
- An `NSNumber` object containing an integer; specifies the maximum zoom level at
- which to cluster points if clustering is enabled. Defaults to one zoom level
- less than the value of `MGLShapeSourceOptionMaximumZoomLevel` so that, at the
- maximum zoom level, the shapes are not clustered.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-clusterMaxZoom"><code>clusterMaxZoom</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering;
-
-/**
- An `NSNumber` object containing an integer; specifies the maximum zoom level at
- which to create vector tiles. A greater value produces greater detail at high
- zoom levels. The default value is 18.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-maxzoom"><code>maxzoom</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel;
-
-/**
- An `NSNumber` object containing an integer; specifies the size of the tile
- buffer on each side. A value of 0 produces no buffer. A value of 512 produces a
- buffer as wide as the tile itself. Larger values produce fewer rendering
- artifacts near tile edges and slower performance. The default value is 128.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-buffer"><code>buffer</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionBuffer;
-
-/**
- An `NSNumber` object containing a double; specifies the Douglas-Peucker
- simplification tolerance. A greater value produces simpler geometries and
- improves performance. The default value is 0.375.
-
- This attribute corresponds to the
- <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson-tolerance"><code>tolerance</code></a>
- source property in the Mapbox Style Specification.
- */
-extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance;
-
-/**
`MGLShapeSource` is a map content source that supplies vector shapes to be
shown on the map. The shapes may be instances of `MGLShape` or `MGLFeature`,
or they may be defined by local or external
<a href="http://geojson.org/">GeoJSON</a> code. A shape source is added to an
`MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
- layer defines the appearance of any content supplied by the shape source.
+ 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.
Each
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson"><code>geojson</code></a>
@@ -111,7 +46,7 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationT
```
*/
MGL_EXPORT
-@interface MGLShapeSource : MGLSource
+@interface MGLShapeSource : MGLAbstractShapeSource
#pragma mark Initializing a Source
diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index 11b1d8eca8..8fbb9a18ef 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -1,4 +1,5 @@
#import "MGLShapeSource_Private.h"
+#import "MGLAbstractShapeSource_Private.h"
#import "MGLStyle_Private.h"
#import "MGLMapView_Private.h"
@@ -11,13 +12,9 @@
#include <mbgl/map/map.hpp>
#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/renderer/renderer.hpp>
+
-const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
-const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius";
-const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
-const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel";
-const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
-const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance";
@interface MGLShapeSource ()
@@ -97,64 +94,10 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
}
std::vector<mbgl::Feature> features;
- if (self.style) {
- features = self.style.mapView.mbglMap->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter });
+ if (self.mapView) {
+ features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { {}, optionalFilter });
}
return MGLFeaturesFromMBGLFeatures(features);
}
@end
-
-mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) {
- auto geoJSONOptions = mbgl::style::GeoJSONOptions();
-
- if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevel]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevel must be an NSNumber."];
- }
- geoJSONOptions.maxzoom = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionBuffer]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionBuffer must be an NSNumber."];
- }
- geoJSONOptions.buffer = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionSimplificationTolerance]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionSimplificationTolerance must be an NSNumber."];
- }
- geoJSONOptions.tolerance = value.doubleValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionClusterRadius]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionClusterRadius must be an NSNumber."];
- }
- geoJSONOptions.clusterRadius = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionMaximumZoomLevelForClustering]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionMaximumZoomLevelForClustering must be an NSNumber."];
- }
- geoJSONOptions.clusterMaxZoom = value.integerValue;
- }
-
- if (NSNumber *value = options[MGLShapeSourceOptionClustered]) {
- if (![value isKindOfClass:[NSNumber class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"MGLShapeSourceOptionClustered must be an NSNumber."];
- }
- geoJSONOptions.cluster = value.boolValue;
- }
-
- return geoJSONOptions;
-}
diff --git a/platform/darwin/src/MGLShapeSource_Private.h b/platform/darwin/src/MGLShapeSource_Private.h
index 84eb5deed4..1ea2c39b8e 100644
--- a/platform/darwin/src/MGLShapeSource_Private.h
+++ b/platform/darwin/src/MGLShapeSource_Private.h
@@ -12,7 +12,4 @@ namespace mbgl {
@interface MGLShapeSource (Private)
@end
-MGL_EXPORT
-mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
-
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLSource.h b/platform/darwin/src/MGLSource.h
index f990aedd67..8d8c936833 100644
--- a/platform/darwin/src/MGLSource.h
+++ b/platform/darwin/src/MGLSource.h
@@ -16,10 +16,10 @@ NS_ASSUME_NONNULL_BEGIN
add and remove sources dynamically using methods such as
`-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`.
- Do not create instances of this class directly, and do not create your own
- subclasses of this class. Instead, create instances of `MGLShapeSource`,
- `MGLImageSource` and the concrete subclasses of `MGLTileSource`,
- `MGLVectorSource` and `MGLRasterSource`.
+ 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.
*/
MGL_EXPORT
@interface MGLSource : NSObject
diff --git a/platform/darwin/src/MGLSource.mm b/platform/darwin/src/MGLSource.mm
index ee012f4d66..6d57e14e8c 100644
--- a/platform/darwin/src/MGLSource.mm
+++ b/platform/darwin/src/MGLSource.mm
@@ -1,7 +1,9 @@
#import "MGLSource_Private.h"
#import "MGLStyle_Private.h"
+#import "MGLMapView_Private.h"
#include <mbgl/style/style.hpp>
+#include <mbgl/map/map.hpp>
#include <mbgl/style/source.hpp>
@interface MGLSource ()
@@ -10,7 +12,7 @@
// special internal source types like mbgl::AnnotationSource.
@property (nonatomic, readonly) mbgl::style::Source *rawSource;
-@property (nonatomic, readonly, weak) MGLStyle *style;
+@property (nonatomic, readonly, weak) MGLMapView *mapView;
@end
@@ -27,37 +29,38 @@
return self;
}
-- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource {
+- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(MGLMapView *)mapView {
NSString *identifier = @(rawSource->getID().c_str());
if (self = [self initWithIdentifier:identifier]) {
_rawSource = rawSource;
_rawSource->peer = SourceWrapper { self };
+ _mapView = mapView;
}
return self;
}
- (instancetype)initWithPendingSource:(std::unique_ptr<mbgl::style::Source>)pendingSource {
- if (self = [self initWithRawSource:pendingSource.get()]) {
+ if (self = [self initWithRawSource:pendingSource.get() mapView:nil]) {
_pendingSource = std::move(pendingSource);
}
return self;
}
-- (void)addToStyle:(MGLStyle *)style {
+- (void)addToMapView:(MGLMapView *)mapView {
if (_pendingSource == nullptr) {
[NSException raise:@"MGLRedundantSourceException"
format:@"This instance %@ was already added to %@. Adding the same source instance " \
- "to the style more than once is invalid.", self, style];
+ "to the style more than once is invalid.", self, mapView.style];
}
-
- _style = style;
- style.rawStyle->addSource(std::move(_pendingSource));
+
+ _mapView = mapView;
+ _mapView.style.rawStyle->addSource(std::move(_pendingSource));
}
-- (void)removeFromStyle:(MGLStyle *)style {
- if (self.rawSource == style.rawStyle->getSource(self.identifier.UTF8String)) {
- _pendingSource = style.rawStyle->removeSource(self.identifier.UTF8String);
- _style = nil;
+- (void)removeFromMapView:(MGLMapView *)mapView {
+ if (self.rawSource == mapView.style.rawStyle->getSource(self.identifier.UTF8String)) {
+ _pendingSource = mapView.style.rawStyle->removeSource(self.identifier.UTF8String);
+ _mapView = nil;
}
}
diff --git a/platform/darwin/src/MGLSource_Private.h b/platform/darwin/src/MGLSource_Private.h
index ba78973279..d7d1f66641 100644
--- a/platform/darwin/src/MGLSource_Private.h
+++ b/platform/darwin/src/MGLSource_Private.h
@@ -18,7 +18,7 @@ struct SourceWrapper {
__weak MGLSource *source;
};
-@class MGLStyle;
+@class MGLMapView;
@interface MGLSource (Private)
@@ -26,7 +26,7 @@ struct SourceWrapper {
Initializes and returns a source with a raw pointer to the backing store,
associated with a style.
*/
-- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource;
+- (instancetype)initWithRawSource:(mbgl::style::Source *)rawSource mapView:(nullable MGLMapView *)mapView;
/**
Initializes and returns a source with an owning pointer to the backing store,
@@ -44,33 +44,30 @@ struct SourceWrapper {
@property (nonatomic, readonly) mbgl::style::Source *rawSource;
/**
- The style which currently contains the source.
-
- If the source is not currently part of a style, this property is
+ The map view whose style currently contains the source.
+ If the source is not currently part of any map view’s style, this property is
set to `nil`.
*/
-@property (nonatomic, readonly, weak) MGLStyle *style;
+@property (nonatomic, readonly, weak) MGLMapView *mapView;
/**
- Adds the mbgl source that this object represents to the style.
-
+ Adds the mbgl source that this object represents to the mbgl map.
Once a mbgl source is added, ownership of the object is transferred to the
- `mbgl::Style` and this object no longer has an active unique_ptr reference to the
+ `mbgl::Map` and this object no longer has an active unique_ptr reference to the
`mbgl::Source`. If this object's mbgl source is in that state, the mbgl source
can still be changed but the changes will not be visible until the `MGLSource`
- is added back to the style via `-[MGLStyle addSource:]` and styled with a
+ is added back to the map via `-[MGLStyle addSource:]` and styled with a
`MGLLayer`.
*/
-- (void)addToStyle:(MGLStyle *)style;
+- (void)addToMapView:(MGLMapView *)mapView;
/**
- Removes the mbgl source that this object represents from the style.
-
+ Removes the mbgl source that this object represents from the mbgl map.
When a mbgl source is removed, ownership of the object is transferred back
to the `MGLSource` instance and the unique_ptr reference is valid again. It is
safe to add the source back to the style after it is removed.
*/
-- (void)removeFromStyle:(MGLStyle *)style;
+- (void)removeFromMapView:(MGLMapView *)mapView;
@end
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
index 6fb4a6cc6b..0b360de8fc 100644
--- a/platform/darwin/src/MGLStyle.h
+++ b/platform/darwin/src/MGLStyle.h
@@ -233,66 +233,38 @@ MGL_EXPORT
+ (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version;
/**
- Returns the URL to the current version of the
+ Returns the URL to version 2 of the
<a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Day</a>
style.
- Traffic Day color-codes roads based on live traffic congestion data. Traffic
- data is currently available in
- <a href="https://www.mapbox.com/api-documentation/pages/traffic-countries.html">these select countries</a>.
-
- @warning The return value may change in a future release of the SDK. If you use
- any feature that depends on a specific aspect of a default style – for
- instance, the minimum zoom level that includes roads – use the
- `-trafficDayStyleURLWithVersion:` method instead. Such details may change
- significantly from version to version.
*/
-+ (NSURL *)trafficDayStyleURL;
++ (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.
-
- Traffic Day color-codes roads based on live traffic congestion data. Traffic
- data is currently available in
- <a href="https://www.mapbox.com/api-documentation/pages/traffic-countries.html">these select countries</a>.
-
+
@param version A specific version of the style.
*/
-+ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version;
++ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));;
/**
- Returns the URL to the current version of the
+ Returns the URL to the version 2 of the
<a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Night</a>
style.
- Traffic Night color-codes roads based on live traffic congestion data and is
- designed to maximize legibility in low-light situations. Traffic data is
- currently available in
- <a href="https://www.mapbox.com/api-documentation/pages/traffic-countries.html">these select countries</a>.
-
- @warning The return value may change in a future release of the SDK. If you use
- any feature that depends on a specific aspect of a default style – for
- instance, the minimum zoom level that includes roads – use the
- `-trafficNightStyleURLWithVersion:` method instead. Such details may change
- significantly from version to version.
*/
-+ (NSURL *)trafficNightStyleURL;
++ (NSURL *)trafficNightStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
/**
- Returns the URL to the given version of the
+ 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.
-
- Traffic Night color-codes roads based on live traffic congestion data and is
- designed to maximize legibility in low-light situations. Traffic data is
- currently available in
- <a href="https://www.mapbox.com/api-documentation/pages/traffic-countries.html">these select countries</a>.
-
+
@param version A specific version of the style.
*/
-+ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version;
++ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
#pragma mark Accessing Metadata About the Style
@@ -557,6 +529,21 @@ MGL_EXPORT
*/
@property (nonatomic, strong) MGLLight *light;
+#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.
+
+ 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;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 2365641f02..f04ae2b104 100644
--- a/platform/darwin/src/MGLStyle.mm
+++ b/platform/darwin/src/MGLStyle.mm
@@ -17,6 +17,7 @@
#import "MGLLight_Private.h"
#import "MGLTileSource_Private.h"
#import "MGLVectorSource.h"
+#import "MGLVectorSource_Private.h"
#import "MGLRasterSource.h"
#import "MGLShapeSource.h"
#import "MGLImageSource.h"
@@ -49,12 +50,35 @@
#import "NSImage+MGLAdditions.h"
#endif
+/**
+ Model class for localization changes.
+ */
+@interface MGLTextLanguage: NSObject
+@property (strong, nonatomic) NSString *originalTextField;
+@property (strong, nonatomic) NSString *updatedTextField;
+
+- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField;
+
+@end
+
+@implementation MGLTextLanguage
+- (instancetype)initWithTextLanguage:(NSString *)originalTextField updatedTextField:(NSString *)updatedTextField
+{
+ if (self = [super init]) {
+ _originalTextField = originalTextField;
+ _updatedTextField = updatedTextField;
+ }
+ return self;
+}
+@end
+
@interface MGLStyle()
@property (nonatomic, readonly, weak) MGLMapView *mapView;
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
@property (readonly, copy, nullable) NSURL *URL;
@property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers;
+@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier;
@end
@@ -84,8 +108,6 @@ MGL_DEFINE_STYLE(light, light)
MGL_DEFINE_STYLE(dark, dark)
MGL_DEFINE_STYLE(satellite, satellite)
MGL_DEFINE_STYLE(satelliteStreets, satellite-streets)
-MGL_DEFINE_STYLE(trafficDay, traffic-day)
-MGL_DEFINE_STYLE(trafficNight, traffic-night)
// Make sure all the styles listed in mbgl::util::default_styles::orderedStyles
// are defined above and also declared in MGLStyle.h.
@@ -112,6 +134,35 @@ static NSURL *MGLStyleURL_emerald;
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 {
@@ -119,6 +170,7 @@ static NSURL *MGLStyleURL_emerald;
_mapView = mapView;
_rawStyle = rawStyle;
_openGLLayers = [NSMutableDictionary dictionary];
+ _localizedLayersByIdentifier = [NSMutableDictionary dictionary];
}
return self;
}
@@ -164,26 +216,27 @@ static NSURL *MGLStyleURL_emerald;
- (MGLSource *)sourceWithIdentifier:(NSString *)identifier
{
auto rawSource = self.rawStyle->getSource(identifier.UTF8String);
+
return rawSource ? [self sourceFromMBGLSource:rawSource] : nil;
}
- (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];
+ return [[MGLVectorSource alloc] initWithRawSource:vectorSource mapView:self.mapView];
} else if (auto geoJSONSource = rawSource->as<mbgl::style::GeoJSONSource>()) {
- return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource];
+ return [[MGLShapeSource alloc] initWithRawSource:geoJSONSource mapView:self.mapView];
} else if (auto rasterSource = rawSource->as<mbgl::style::RasterSource>()) {
- return [[MGLRasterSource alloc] initWithRawSource:rasterSource];
+ return [[MGLRasterSource alloc] initWithRawSource:rasterSource mapView:self.mapView];
} else if (auto imageSource = rawSource->as<mbgl::style::ImageSource>()) {
- return [[MGLImageSource alloc] initWithRawSource:imageSource];
+ return [[MGLImageSource alloc] initWithRawSource:imageSource mapView:self.mapView];
} else {
- return [[MGLSource alloc] initWithRawSource:rawSource];
+ return [[MGLSource alloc] initWithRawSource:rawSource mapView:self.mapView];
}
}
@@ -197,7 +250,7 @@ static NSURL *MGLStyleURL_emerald;
}
try {
- [source addToStyle:self];
+ [source addToMapView:self.mapView];
} catch (std::runtime_error & err) {
[NSException raise:@"MGLRedundantSourceIdentifierException" format:@"%s", err.what()];
}
@@ -211,7 +264,7 @@ static NSURL *MGLStyleURL_emerald;
@"Make sure the source was created as a member of a concrete subclass of MGLSource.",
source];
}
- [source removeFromStyle:self];
+ [source removeFromMapView:self.mapView];
}
- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
@@ -330,7 +383,7 @@ static NSURL *MGLStyleURL_emerald;
{
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;
}
@@ -585,4 +638,140 @@ static NSURL *MGLStyleURL_emerald;
self.URL ? [NSString stringWithFormat:@"\"%@\"", self.URL] : self.URL];
}
+#pragma mark Mapbox Streets source introspection
+
+- (void)setLocalizesLabels:(BOOL)localizesLabels
+{
+ if (_localizesLabels != localizesLabels) {
+ _localizesLabels = localizesLabels;
+ } else {
+ return;
+ }
+
+ 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;
+ }
+ }
+ } 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;
+ }];
+}
+
+- (NS_ARRAY_OF(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];
+ NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MGLVectorStyleLayer * _Nullable layer, NSDictionary<NSString *, id> * _Nullable bindings) {
+ return [layer isKindOfClass:[MGLVectorStyleLayer class]] && [streetsSourceIdentifiers containsObject:layer.sourceIdentifier] && [placeSourceLayerIdentifiers containsObject:layer.sourceLayerIdentifier];
+ }];
+ return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
+}
+
+- (NS_ARRAY_OF(MGLStyleLayer *) *)roadStyleLayers {
+ NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
+
+ NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MGLVectorStyleLayer * _Nullable layer, NSDictionary<NSString *, id> * _Nullable bindings) {
+ return [layer isKindOfClass:[MGLVectorStyleLayer class]] && [streetsSourceIdentifiers containsObject:layer.sourceIdentifier] && [layer.sourceLayerIdentifier isEqualToString:@"road_label"];
+ }];
+ return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
+}
+
@end
diff --git a/platform/darwin/src/MGLStyleLayer.h b/platform/darwin/src/MGLStyleLayer.h
index 7d181667d6..b610a27607 100644
--- a/platform/darwin/src/MGLStyleLayer.h
+++ b/platform/darwin/src/MGLStyleLayer.h
@@ -14,10 +14,10 @@ NS_ASSUME_NONNULL_BEGIN
`MGLStyleLayer` object, which you can use to refine the map’s appearance. You
can also add and remove style layers dynamically.
- Do not create instances of this class directly, and do not create your own
- subclasses of this class. Instead, create instances of
- `MGLBackgroundStyleLayer` and the concrete subclasses of
- `MGLForegroundStyleLayer`.
+ Create instances of `MGLBackgroundStyleLayer` and the concrete subclasses of
+ `MGLForegroundStyleLayer` in order to use `MGLStyleLayer`'s properties and methods.
+ You do not create instances of `MGLStyleLayer` directly, and do not
+ create your own subclasses of this class.
Do not add `MGLStyleLayer` objects to the `style` property of a `MGLMapView` before
`-mapView:didFinishLoadingStyle:` is called.
diff --git a/platform/darwin/src/MGLStyleValue.h b/platform/darwin/src/MGLStyleValue.h
index 2bb3aca4f4..9c9b1dc4d1 100644
--- a/platform/darwin/src/MGLStyleValue.h
+++ b/platform/darwin/src/MGLStyleValue.h
@@ -239,11 +239,10 @@ MGL_EXPORT
defined by an `MGLCameraStyleFunction`, `MGLSourceStyleFunction`, or
`MGLCompositeStyleFunction` object.
- Do not create instances of this class directly, and do not create your own
- subclasses of this class. Instead, use one of the class factory methods in
- `MGLStyleValue` to create instances of the following concrete subclasses:
- `MGLCameraStyleFunction`, `MGLSourceStyleFunction`, and
- `MGLCompositeStyleFunction`.
+ 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.
diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h
index 2155c657bd..5914e0a2aa 100644
--- a/platform/darwin/src/MGLStyleValue_Private.h
+++ b/platform/darwin/src/MGLStyleValue_Private.h
@@ -124,9 +124,9 @@ public:
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);
+ auto result = mbgl::style::conversion::convert<mbgl::style::DataDrivenPropertyValue<MBGLType>>(
+ mbgl::style::conversion::makeConvertible(toRawStyleSpecValue((MGLStyleFunction<ObjCType> *) value)), error);
NSCAssert(result, @(error.message.c_str()));
return *result;
} else {
diff --git a/platform/darwin/src/MGLStyle_Private.h b/platform/darwin/src/MGLStyle_Private.h
index 92b08e844b..e5bd79dc02 100644
--- a/platform/darwin/src/MGLStyle_Private.h
+++ b/platform/darwin/src/MGLStyle_Private.h
@@ -14,6 +14,8 @@ namespace mbgl {
@class MGLAttributionInfo;
@class MGLMapView;
@class MGLOpenGLStyleLayer;
+@class MGLVectorSource;
+@class MGLVectorStyleLayer;
@interface MGLStyle (Private)
@@ -30,4 +32,11 @@ namespace mbgl {
@end
+@interface MGLStyle (MGLStreetsAdditions)
+
+@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *placeStyleLayers;
+@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *roadStyleLayers;
+
+@end
+
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index 5df995aa01..ffb95dfc73 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -8,6 +8,72 @@
NS_ASSUME_NONNULL_BEGIN
/**
+ Part of the icon placed closest to the anchor.
+
+ Values of this type are used in the `MGLSymbolStyleLayer.iconAnchor`
+ property.
+ */
+typedef NS_ENUM(NSUInteger, MGLIconAnchor) {
+ /**
+ The center of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorCenter,
+ /**
+ The left side of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorLeft,
+ /**
+ The right side of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorRight,
+ /**
+ The top of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorTop,
+ /**
+ The bottom of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorBottom,
+ /**
+ The top left corner of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorTopLeft,
+ /**
+ The top right corner of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorTopRight,
+ /**
+ The bottom left corner of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorBottomLeft,
+ /**
+ The bottom right corner of the icon is placed closest to the anchor.
+ */
+ MGLIconAnchorBottomRight,
+};
+
+/**
+ Orientation of icon when map is pitched.
+
+ Values of this type are used in the `MGLSymbolStyleLayer.iconPitchAlignment`
+ property.
+ */
+typedef NS_ENUM(NSUInteger, MGLIconPitchAlignment) {
+ /**
+ The icon is aligned to the plane of the map.
+ */
+ MGLIconPitchAlignmentMap,
+ /**
+ The icon is aligned to the plane of the viewport.
+ */
+ MGLIconPitchAlignmentViewport,
+ /**
+ Automatically matches the value of `iconRotationAlignment`.
+ */
+ MGLIconPitchAlignmentAuto,
+};
+
+/**
In combination with `symbolPlacement`, determines the rotation behavior of
icons.
@@ -324,6 +390,34 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *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.
+
+ 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;
+
+/**
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
@@ -477,6 +571,24 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *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.
+
+ 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
+ `MGLInterpolationModeInterval`
+ */
+@property (nonatomic, null_resettable) MGLStyleValue<NSValue *> *iconPitchAlignment;
+
+/**
Rotates the icon clockwise.
This property is measured in degrees.
@@ -745,6 +857,15 @@ MGL_EXPORT
* `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<NSNumber *> *maximumTextWidth;
@@ -885,8 +1006,18 @@ MGL_EXPORT
You can set this property to an instance of:
* `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * `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;
@@ -1004,8 +1135,18 @@ MGL_EXPORT
You can set this property to an instance of:
* `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * `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 *> *textJustification;
@@ -1030,6 +1171,15 @@ MGL_EXPORT
* `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<NSNumber *> *textLetterSpacing;
@@ -1925,6 +2075,32 @@ MGL_EXPORT
#pragma mark Working with Symbol Style Layer Attribute Values
/**
+ Creates a new value object containing the given `MGLIconAnchor` enumeration.
+
+ @param iconAnchor The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLIconAnchor:(MGLIconAnchor)iconAnchor;
+
+/**
+ The `MGLIconAnchor` enumeration representation of the value.
+ */
+@property (readonly) MGLIconAnchor MGLIconAnchorValue;
+
+/**
+ Creates a new value object containing the given `MGLIconPitchAlignment` enumeration.
+
+ @param iconPitchAlignment The value for the new object.
+ @return A new value object that contains the enumeration value.
+ */
++ (instancetype)valueWithMGLIconPitchAlignment:(MGLIconPitchAlignment)iconPitchAlignment;
+
+/**
+ The `MGLIconPitchAlignment` enumeration representation of the value.
+ */
+@property (readonly) MGLIconPitchAlignment MGLIconPitchAlignmentValue;
+
+/**
Creates a new value object containing the given `MGLIconRotationAlignment` enumeration.
@param iconRotationAlignment The value for the new object.
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index 5a8f8c6084..1990c82669 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -13,6 +13,24 @@
namespace mbgl {
+ MBGL_DEFINE_ENUM(MGLIconAnchor, {
+ { MGLIconAnchorCenter, "center" },
+ { MGLIconAnchorLeft, "left" },
+ { MGLIconAnchorRight, "right" },
+ { MGLIconAnchorTop, "top" },
+ { MGLIconAnchorBottom, "bottom" },
+ { MGLIconAnchorTopLeft, "top-left" },
+ { MGLIconAnchorTopRight, "top-right" },
+ { MGLIconAnchorBottomLeft, "bottom-left" },
+ { MGLIconAnchorBottomRight, "bottom-right" },
+ });
+
+ MBGL_DEFINE_ENUM(MGLIconPitchAlignment, {
+ { MGLIconPitchAlignmentMap, "map" },
+ { MGLIconPitchAlignmentViewport, "viewport" },
+ { MGLIconPitchAlignmentAuto, "auto" },
+ });
+
MBGL_DEFINE_ENUM(MGLIconRotationAlignment, {
{ MGLIconRotationAlignmentMap, "map" },
{ MGLIconRotationAlignmentViewport, "viewport" },
@@ -160,6 +178,23 @@ namespace mbgl {
return self.iconAllowsOverlap;
}
+- (void)setIconAnchor:(MGLStyleValue<NSValue *> *)iconAnchor {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenPropertyValue(iconAnchor);
+ self.rawLayer->setIconAnchor(mbglValue);
+}
+
+- (MGLStyleValue<NSValue *> *)iconAnchor {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getIconAnchor();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultIconAnchor());
+ }
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLIconAnchor>().toDataDrivenStyleValue(propertyValue);
+}
+
- (void)setIconIgnoresPlacement:(MGLStyleValue<NSNumber *> *)iconIgnoresPlacement {
MGLAssertStyleLayerIsValid();
@@ -259,6 +294,23 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
+- (void)setIconPitchAlignment:(MGLStyleValue<NSValue *> *)iconPitchAlignment {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumPropertyValue(iconPitchAlignment);
+ self.rawLayer->setIconPitchAlignment(mbglValue);
+}
+
+- (MGLStyleValue<NSValue *> *)iconPitchAlignment {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getIconPitchAlignment();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultIconPitchAlignment());
+ }
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumStyleValue(propertyValue);
+}
+
- (void)setIconRotation:(MGLStyleValue<NSNumber *> *)iconRotation {
MGLAssertStyleLayerIsValid();
@@ -433,7 +485,7 @@ namespace mbgl {
- (void)setMaximumTextWidth:(MGLStyleValue<NSNumber *> *)maximumTextWidth {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(maximumTextWidth);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(maximumTextWidth);
self.rawLayer->setTextMaxWidth(mbglValue);
}
@@ -442,9 +494,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextMaxWidth();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextMaxWidth());
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextMaxWidth());
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextMaxWidth:(MGLStyleValue<NSNumber *> *)textMaxWidth {
@@ -563,7 +615,7 @@ namespace mbgl {
- (void)setTextAnchor:(MGLStyleValue<NSValue *> *)textAnchor {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toEnumPropertyValue(textAnchor);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenPropertyValue(textAnchor);
self.rawLayer->setTextAnchor(mbglValue);
}
@@ -572,9 +624,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextAnchor();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toEnumStyleValue(self.rawLayer->getDefaultTextAnchor());
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextAnchor());
}
- return MGLStyleValueTransformer<mbgl::style::TextAnchorType, NSValue *, mbgl::style::TextAnchorType, MGLTextAnchor>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::SymbolAnchorType, NSValue *, mbgl::style::SymbolAnchorType, MGLTextAnchor>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextFontNames:(MGLStyleValue<NSArray<NSString *> *> *)textFontNames {
@@ -652,7 +704,7 @@ namespace mbgl {
- (void)setTextJustification:(MGLStyleValue<NSValue *> *)textJustification {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toEnumPropertyValue(textJustification);
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenPropertyValue(textJustification);
self.rawLayer->setTextJustify(mbglValue);
}
@@ -661,9 +713,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextJustify();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toEnumStyleValue(self.rawLayer->getDefaultTextJustify());
+ return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextJustify());
}
- return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toEnumStyleValue(propertyValue);
+ return MGLStyleValueTransformer<mbgl::style::TextJustifyType, NSValue *, mbgl::style::TextJustifyType, MGLTextJustification>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextJustify:(MGLStyleValue<NSValue *> *)textJustify {
@@ -676,7 +728,7 @@ namespace mbgl {
- (void)setTextLetterSpacing:(MGLStyleValue<NSNumber *> *)textLetterSpacing {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toInterpolatablePropertyValue(textLetterSpacing);
+ auto mbglValue = MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenPropertyValue(textLetterSpacing);
self.rawLayer->setTextLetterSpacing(mbglValue);
}
@@ -685,9 +737,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextLetterSpacing();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(self.rawLayer->getDefaultTextLetterSpacing());
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextLetterSpacing());
}
- return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<float, NSNumber *>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextLineHeight:(MGLStyleValue<NSNumber *> *)textLineHeight {
@@ -1321,6 +1373,26 @@ namespace mbgl {
@implementation NSValue (MGLSymbolStyleLayerAdditions)
++ (NSValue *)valueWithMGLIconAnchor:(MGLIconAnchor)iconAnchor {
+ return [NSValue value:&iconAnchor withObjCType:@encode(MGLIconAnchor)];
+}
+
+- (MGLIconAnchor)MGLIconAnchorValue {
+ MGLIconAnchor iconAnchor;
+ [self getValue:&iconAnchor];
+ return iconAnchor;
+}
+
++ (NSValue *)valueWithMGLIconPitchAlignment:(MGLIconPitchAlignment)iconPitchAlignment {
+ return [NSValue value:&iconPitchAlignment withObjCType:@encode(MGLIconPitchAlignment)];
+}
+
+- (MGLIconPitchAlignment)MGLIconPitchAlignmentValue {
+ MGLIconPitchAlignment iconPitchAlignment;
+ [self getValue:&iconPitchAlignment];
+ return iconPitchAlignment;
+}
+
+ (NSValue *)valueWithMGLIconRotationAlignment:(MGLIconRotationAlignment)iconRotationAlignment {
return [NSValue value:&iconRotationAlignment withObjCType:@encode(MGLIconRotationAlignment)];
}
diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h
index 538b94037e..caeafcd2f6 100644
--- a/platform/darwin/src/MGLTileSource.h
+++ b/platform/darwin/src/MGLTileSource.h
@@ -140,9 +140,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>.
- Do not create instances of this class directly, and do not create your own
- subclasses of this class. Instead, create instances of `MGLRasterSource` and
- `MGLVectorSource`.
+ 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.
*/
MGL_EXPORT
@interface MGLTileSource : MGLSource
diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h
index 16f510b5a6..5c32791c2f 100644
--- a/platform/darwin/src/MGLTypes.h
+++ b/platform/darwin/src/MGLTypes.h
@@ -47,6 +47,8 @@ typedef NS_ENUM(NSInteger, MGLErrorCode) {
MGLErrorCodeParseStyleFailed = 4,
/** An attempt to load the style failed. */
MGLErrorCodeLoadStyleFailed = 5,
+ /** An error occurred while snapshotting the map. */
+ MGLErrorCodeSnapshotFailed = 6,
};
/** Options for enabling debugging features in an `MGLMapView` instance. */
@@ -78,7 +80,7 @@ typedef NS_OPTIONS(NSUInteger, MGLMapDebugMaskOptions) {
/**
A structure containing information about a transition.
*/
-typedef struct MGLTransition {
+typedef struct __attribute__((objc_boxable)) MGLTransition {
/**
The amount of time the animation should take, not including the delay.
*/
diff --git a/platform/darwin/src/MGLVectorSource.mm b/platform/darwin/src/MGLVectorSource.mm
index 7265690f4d..90ffe6f98b 100644
--- a/platform/darwin/src/MGLVectorSource.mm
+++ b/platform/darwin/src/MGLVectorSource.mm
@@ -5,11 +5,13 @@
#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 ()
@@ -63,10 +65,62 @@
}
std::vector<mbgl::Feature> features;
- if (self.style) {
- features = self.style.mapView.mbglMap->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
+ if (self.mapView) {
+ features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
}
return MGLFeaturesFromMBGLFeatures(features);
}
@end
+
+@implementation MGLVectorSource (Private)
+
++ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages {
+ // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
+ static dispatch_once_t onceToken;
+ static NS_SET_OF(NSString *) *mapboxStreetsLanguages;
+ dispatch_once(&onceToken, ^{
+ // https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
+ mapboxStreetsLanguages = [NSSet setWithObjects:@"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", nil];
+ });
+ return mapboxStreetsLanguages;
+}
+
++ (NSString *)preferredMapboxStreetsLanguage {
+ NSArray<NSString *> *supportedLanguages = [MGLVectorSource mapboxStreetsLanguages].allObjects;
+ NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:supportedLanguages
+ forPreferences:[NSLocale preferredLanguages]];
+ NSString *mostSpecificLanguage;
+ for (NSString *language in preferredLanguages) {
+ if (language.length > mostSpecificLanguage.length) {
+ mostSpecificLanguage = language;
+ }
+ }
+ return mostSpecificLanguage ?: @"en";
+}
+
+- (BOOL)isMapboxStreets {
+ NSURL *url = self.configurationURL;
+ if (![url.scheme isEqualToString:@"mapbox"]) {
+ return NO;
+ }
+ NSArray *identifiers = [url.host componentsSeparatedByString:@","];
+ return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"];
+}
+
+- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage {
+ if (!self.mapboxStreets) {
+ return @{};
+ }
+
+ // Replace {name} and {name_*} with the matching localized name tag.
+ NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name";
+ NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"];
+ for (NSString *languageCode in [MGLVectorSource mapboxStreetsLanguages]) {
+ NSString *key = [NSString stringWithFormat:@"name_%@", languageCode];
+ localizedKeysByKey[key] = localizedKey;
+ }
+ return localizedKeysByKey;
+}
+
+@end
diff --git a/platform/darwin/src/MGLVectorSource_Private.h b/platform/darwin/src/MGLVectorSource_Private.h
index 335743173e..233809aea2 100644
--- a/platform/darwin/src/MGLVectorSource_Private.h
+++ b/platform/darwin/src/MGLVectorSource_Private.h
@@ -3,6 +3,13 @@
NS_ASSUME_NONNULL_BEGIN
@interface MGLVectorSource (Private)
+
+@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets;
+
++ (NSString *)preferredMapboxStreetsLanguage;
+
+- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLVectorStyleLayer.h b/platform/darwin/src/MGLVectorStyleLayer.h
index c6193e6046..6603570e25 100644
--- a/platform/darwin/src/MGLVectorStyleLayer.h
+++ b/platform/darwin/src/MGLVectorStyleLayer.h
@@ -9,10 +9,11 @@ NS_ASSUME_NONNULL_BEGIN
`MGLVectorStyleLayer` is an abstract superclass for style layers whose content
is defined by an `MGLShapeSource` or `MGLVectorSource` object.
- Do not create instances of this class directly, and do not create your own
- subclasses of this class. Instead, create instances of the following concrete
- subclasses: `MGLCircleStyleLayer`, `MGLFillStyleLayer`, `MGLLineStyleLayer`,
- and `MGLSymbolStyleLayer`.
+ 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.
*/
MGL_EXPORT
@interface MGLVectorStyleLayer : MGLForegroundStyleLayer
diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m
index f383a50300..f55059324e 100644
--- a/platform/darwin/src/NSBundle+MGLAdditions.m
+++ b/platform/darwin/src/NSBundle+MGLAdditions.m
@@ -15,7 +15,7 @@
bundle = [self bundleWithPath:bundlePath];
} else {
[NSException raise:@"MGLBundleNotFoundException" format:
- @"The Mapbox framework bundle could not be found. If using the Mapbox iOS SDK as a static framework, make sure that Mapbox.bundle is copied into the root of the app bundle."];
+ @"The Mapbox framework bundle could not be found. If using the Mapbox Maps SDK for iOS as a static framework, make sure that Mapbox.bundle is copied into the root of the app bundle."];
}
}
diff --git a/platform/darwin/src/NSString+MGLAdditions.m b/platform/darwin/src/NSString+MGLAdditions.m
index 371ef4023e..cde4bddcc3 100644
--- a/platform/darwin/src/NSString+MGLAdditions.m
+++ b/platform/darwin/src/NSString+MGLAdditions.m
@@ -12,7 +12,16 @@
- (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale {
NSMutableString *string = self.mutableCopy;
- [string enumerateLinguisticTagsInRange:string.mgl_wholeRange scheme:NSLinguisticTagSchemeLexicalClass options:0 orthography:nil usingBlock:^(NSString * _Nonnull tag, NSRange tokenRange, NSRange sentenceRange, BOOL * _Nonnull stop) {
+ 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
+ [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
|| !([tag isEqualToString:NSLinguisticTagConjunction]
diff --git a/platform/darwin/src/headless_backend_cgl.cpp b/platform/darwin/src/headless_backend_cgl.cpp
index 6ad98f4326..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.reset(new HeadlessDisplay);
- }
- return bool(display);
-}
-
-void HeadlessBackend::createContext() {
- assert(!hasContext());
-
- CGLContextObj glContext = nullptr;
- CGLError error = CGLCreateContext(display->attribute<CGLPixelFormatObj>(), nullptr, &glContext);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error creating GL context object:") +
- CGLErrorString(error) + "\n");
- }
-
- error = CGLEnable(glContext, kCGLCEMPEngine);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error enabling OpenGL multithreading:") +
- CGLErrorString(error) + "\n");
- }
-
- impl.reset(new CGLImpl(glContext));
+void HeadlessBackend::createImpl() {
+ assert(!impl);
+ impl = std::make_unique<CGLBackendImpl>();
}
} // namespace mbgl
diff --git a/platform/darwin/src/headless_backend_eagl.mm b/platform/darwin/src/headless_backend_eagl.mm
index 1daaeaf54c..050fa62c78 100644
--- a/platform/darwin/src/headless_backend_eagl.mm
+++ b/platform/darwin/src/headless_backend_eagl.mm
@@ -6,51 +6,48 @@
namespace mbgl {
-struct EAGLImpl : public HeadlessBackend::Impl {
- EAGLImpl(EAGLContext* glContext_) : glContext(glContext_) {
- [reinterpret_cast<EAGLContext*>(glContext) retain];
- reinterpret_cast<EAGLContext*>(glContext).multiThreaded = YES;
+class EAGLBackendImpl : public HeadlessBackend::Impl {
+public:
+ EAGLBackendImpl() {
+ glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+ if (glContext == nil) {
+ throw std::runtime_error("Error creating GL context object");
+ }
+ glContext.multiThreaded = YES;
}
- ~EAGLImpl() {
- [glContext release];
+ // Required for ARC to deallocate correctly.
+ ~EAGLBackendImpl() final = default;
+
+ gl::ProcAddress getExtensionFunctionPointer(const char* name) final {
+ static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles"));
+ if (!framework) {
+ throw std::runtime_error("Failed to load OpenGL framework.");
+ }
+
+ CFStringRef str =
+ CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
+ void* symbol = CFBundleGetFunctionPointerForName(framework, str);
+ CFRelease(str);
+
+ return reinterpret_cast<gl::ProcAddress>(symbol);
}
- void activateContext() {
+ void activateContext() final {
[EAGLContext setCurrentContext:glContext];
}
- void deactivateContext() {
+ void deactivateContext() final {
[EAGLContext setCurrentContext:nil];
}
+private:
EAGLContext* glContext = nullptr;
};
-gl::ProcAddress HeadlessBackend::initializeExtension(const char* name) {
- static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengles"));
- if (!framework) {
- throw std::runtime_error("Failed to load OpenGL framework.");
- }
-
- CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingASCII);
- void* symbol = CFBundleGetFunctionPointerForName(framework, str);
- CFRelease(str);
-
- return reinterpret_cast<gl::ProcAddress>(symbol);
-}
-
-bool HeadlessBackend::hasDisplay() {
- return true;
-}
-
-void HeadlessBackend::createContext() {
- EAGLContext* glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
- if (glContext == nil) {
- throw std::runtime_error("Error creating GL context object");
- }
-
- impl.reset(new EAGLImpl(glContext));
+void HeadlessBackend::createImpl() {
+ assert(!impl);
+ impl = std::make_unique<EAGLBackendImpl>();
}
} // namespace mbgl
diff --git a/platform/darwin/src/headless_display_cgl.cpp b/platform/darwin/src/headless_display_cgl.cpp
deleted file mode 100644
index 5b7a1f2bac..0000000000
--- a/platform/darwin/src/headless_display_cgl.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <mbgl/gl/headless_display.hpp>
-
-#include <OpenGL/OpenGL.h>
-
-#include <stdexcept>
-#include <string>
-
-namespace mbgl {
-
-class HeadlessDisplay::Impl {
-public:
- Impl();
- ~Impl();
- CGLPixelFormatObj pixelFormat = nullptr;
-};
-
-HeadlessDisplay::Impl::Impl() {
- // TODO: test if OpenGL 4.1 with GL_ARB_ES2_compatibility is supported
- // If it is, use kCGLOGLPVersion_3_2_Core and enable that extension.
- CGLPixelFormatAttribute attributes[] = {
- kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_Legacy),
- // Not adding kCGLPFAAccelerated, as this will make headless rendering impossible when running in VMs.
- kCGLPFAClosestPolicy,
- kCGLPFAAccumSize, static_cast<CGLPixelFormatAttribute>(32),
- kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24),
- kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8),
- kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(16),
- kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8),
- kCGLPFASupportsAutomaticGraphicsSwitching,
- kCGLPFAAllowOfflineRenderers, // Allows using the integrated GPU
- static_cast<CGLPixelFormatAttribute>(0)
- };
-
- GLint num;
- CGLError error = CGLChoosePixelFormat(attributes, &pixelFormat, &num);
- if (error != kCGLNoError) {
- throw std::runtime_error(std::string("Error choosing pixel format:") + CGLErrorString(error) + "\n");
- }
- if (num <= 0) {
- throw std::runtime_error("No pixel formats found.");
- }
-}
-
-HeadlessDisplay::Impl::~Impl() {
- CGLDestroyPixelFormat(pixelFormat);
-}
-
-template <>
-CGLPixelFormatObj HeadlessDisplay::attribute() const {
- return impl->pixelFormat;
-}
-
-HeadlessDisplay::HeadlessDisplay()
- : impl(std::make_unique<Impl>()) {
-}
-
-HeadlessDisplay::~HeadlessDisplay() {
-}
-
-} // namespace mbgl
diff --git a/platform/darwin/src/http_file_source.mm b/platform/darwin/src/http_file_source.mm
index 649cebb47f..4a16ad82fb 100644
--- a/platform/darwin/src/http_file_source.mm
+++ b/platform/darwin/src/http_file_source.mm
@@ -266,7 +266,9 @@ std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource,
NSDictionary *headers = [(NSHTTPURLResponse *)res allHeaderFields];
NSString *cache_control = [headers objectForKey:@"Cache-Control"];
if (cache_control) {
- response.expires = http::CacheControl::parse([cache_control UTF8String]).toTimePoint();
+ const auto cc = http::CacheControl::parse([cache_control UTF8String]);
+ response.expires = cc.toTimePoint();
+ response.mustRevalidate = cc.mustRevalidate;
}
NSString *expires = [headers objectForKey:@"Expires"];
diff --git a/platform/darwin/src/image.mm b/platform/darwin/src/image.mm
index 57b680fbdb..3a5adcca0a 100644
--- a/platform/darwin/src/image.mm
+++ b/platform/darwin/src/image.mm
@@ -2,19 +2,7 @@
#import <ImageIO/ImageIO.h>
-namespace {
-
-template <typename T, typename S, void (*Releaser)(S)>
-struct CFHandle {
- CFHandle(T t_): t(t_) {}
- ~CFHandle() { Releaser(t); }
- T operator*() { return t; }
- operator bool() { return t; }
-private:
- T t;
-};
-
-} // namespace
+#import "CFHandle.hpp"
using CGImageHandle = CFHandle<CGImageRef, CGImageRef, CGImageRelease>;
using CFDataHandle = CFHandle<CFDataRef, CFTypeRef, CFRelease>;
@@ -23,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/local_glyph_rasterizer.mm b/platform/darwin/src/local_glyph_rasterizer.mm
new file mode 100644
index 0000000000..14cee5063e
--- /dev/null
+++ b/platform/darwin/src/local_glyph_rasterizer.mm
@@ -0,0 +1,175 @@
+#include <mbgl/text/local_glyph_rasterizer.hpp>
+#include <mbgl/util/i18n.hpp>
+#include <mbgl/util/platform.hpp>
+
+#include <unordered_map>
+
+#import <Foundation/Foundation.h>
+#import <CoreText/CoreText.h>
+#import <ImageIO/ImageIO.h>
+
+#import "CFHandle.hpp"
+
+namespace mbgl {
+
+/*
+ Darwin implementation of LocalGlyphRasterizer:
+ Draws CJK glyphs using locally available fonts.
+
+ Mirrors GL JS implementation in that:
+ - Only CJK glyphs are drawn locally (because we can guess their metrics effectively)
+ * Render size/metrics determined experimentally by rendering a few different fonts
+ - Configuration is done at map creation time by setting a "font family"
+ * JS uses a CSS font-family, this uses kCTFontFamilyNameAttribute which has
+ somewhat different behavior.
+
+ Further improvements are possible:
+ - GL JS heuristically determines a font weight based on the strings included in
+ the FontStack. Android follows a simpler heuristic that just picks up the
+ "Bold" property from the FontStack. Although both should be possible with CoreText,
+ our initial implementation couldn't reliably control the font-weight, so we're
+ skipping that functionality on darwin.
+ (See commit history for attempted implementation)
+ - If we could reliably extract glyph metrics, we wouldn't be limited to CJK glyphs
+ - We could push the font configuration down to individual style layers, which would
+ allow any current style to be reproducible using local fonts.
+ - Instead of just exposing "font family" as a configuration, we could expose a richer
+ CTFontDescriptor configuration option (although we'd have to override font size to
+ make sure it stayed at 24pt).
+ - Because Apple exposes glyph paths via `CTFontCreatePathForGlyph` we could potentially
+ render directly to SDF instead of going through TinySDF -- although it's not clear
+ how much of an improvement it would be.
+*/
+
+using CGColorSpaceHandle = CFHandle<CGColorSpaceRef, CGColorSpaceRef, CGColorSpaceRelease>;
+using CGContextHandle = CFHandle<CGContextRef, CGContextRef, CGContextRelease>;
+using CFStringRefHandle = CFHandle<CFStringRef, CFTypeRef, CFRelease>;
+using CFAttributedStringRefHandle = CFHandle<CFAttributedStringRef, CFTypeRef, CFRelease>;
+using CFDictionaryRefHandle = CFHandle<CFDictionaryRef, CFTypeRef, CFRelease>;
+using CTFontDescriptorRefHandle = CFHandle<CTFontDescriptorRef, CFTypeRef, CFRelease>;
+using CTLineRefHandle = CFHandle<CTLineRef, CFTypeRef, CFRelease>;
+
+class LocalGlyphRasterizer::Impl {
+public:
+ Impl(const optional<std::string> fontFamily_)
+ : fontFamily(fontFamily_)
+ , fontHandle(NULL)
+ {}
+
+ ~Impl() {
+ if (fontHandle) {
+ CFRelease(fontHandle);
+ }
+ }
+
+
+ CTFontRef getFont() {
+ if (!fontFamily) {
+ return NULL;
+ }
+
+ if (!fontHandle) {
+ NSDictionary *fontAttributes = @{
+ (NSString *)kCTFontSizeAttribute: [NSNumber numberWithFloat:24.0],
+ (NSString *)kCTFontFamilyNameAttribute: [[NSString alloc] initWithCString:fontFamily->c_str() encoding:NSUTF8StringEncoding]
+ };
+
+ CTFontDescriptorRefHandle descriptor(CTFontDescriptorCreateWithAttributes((CFDictionaryRef)fontAttributes));
+ fontHandle = CTFontCreateWithFontDescriptor(*descriptor, 0.0, NULL);
+ if (!fontHandle) {
+ throw std::runtime_error("CTFontCreateWithFontDescriptor failed");
+ }
+ }
+ return fontHandle;
+ }
+
+private:
+ optional<std::string> fontFamily;
+ CTFontRef fontHandle;
+};
+
+LocalGlyphRasterizer::LocalGlyphRasterizer(const optional<std::string> fontFamily)
+ : impl(std::make_unique<Impl>(fontFamily))
+{}
+
+LocalGlyphRasterizer::~LocalGlyphRasterizer()
+{}
+
+bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID glyphID) {
+ return util::i18n::allowsFixedWidthGlyphGeneration(glyphID) && impl->getFont();
+}
+
+PremultipliedImage drawGlyphBitmap(GlyphID glyphID, CTFontRef font, Size size) {
+ PremultipliedImage rgbaBitmap(size);
+
+ CFStringRefHandle string(CFStringCreateWithCharacters(NULL, reinterpret_cast<UniChar*>(&glyphID), 1));
+
+ CGColorSpaceHandle colorSpace(CGColorSpaceCreateDeviceRGB());
+ if (!colorSpace) {
+ throw std::runtime_error("CGColorSpaceCreateDeviceRGB failed");
+ }
+
+ constexpr const size_t bitsPerComponent = 8;
+ constexpr const size_t bytesPerPixel = 4;
+ const size_t bytesPerRow = bytesPerPixel * size.width;
+
+ CGContextHandle context(CGBitmapContextCreate(
+ rgbaBitmap.data.get(),
+ size.width,
+ size.height,
+ bitsPerComponent,
+ bytesPerRow,
+ *colorSpace,
+ kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast));
+ if (!context) {
+ throw std::runtime_error("CGBitmapContextCreate failed");
+ }
+
+ CFStringRef keys[] = { kCTFontAttributeName };
+ CFTypeRef values[] = { font };
+
+ CFDictionaryRefHandle attributes(
+ CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
+ (const void**)&values, sizeof(keys) / sizeof(keys[0]),
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ CFAttributedStringRefHandle attrString(CFAttributedStringCreate(kCFAllocatorDefault, *string, *attributes));
+ CTLineRefHandle line(CTLineCreateWithAttributedString(*attrString));
+
+ // Start drawing a little bit below the top of the bitmap
+ CGContextSetTextPosition(*context, 0.0, 5.0);
+ CTLineDraw(*line, *context);
+
+ return rgbaBitmap;
+}
+
+Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack&, GlyphID glyphID) {
+ Glyph fixedMetrics;
+ CTFontRef font = impl->getFont();
+ if (!font) {
+ return fixedMetrics;
+ }
+
+ fixedMetrics.id = glyphID;
+
+ Size size(35, 35);
+
+ fixedMetrics.metrics.width = size.width;
+ fixedMetrics.metrics.height = size.height;
+ fixedMetrics.metrics.left = 3;
+ fixedMetrics.metrics.top = -1;
+ fixedMetrics.metrics.advance = 24;
+
+ PremultipliedImage rgbaBitmap = drawGlyphBitmap(glyphID, font, size);
+
+ // Copy alpha values from RGBA bitmap into the AlphaImage output
+ fixedMetrics.bitmap = AlphaImage(size);
+ for (uint32_t i = 0; i < size.width * size.height; i++) {
+ fixedMetrics.bitmap.data[i] = rgbaBitmap.data[4 * i + 3];
+ }
+
+ return fixedMetrics;
+}
+
+} // namespace mbgl
diff --git a/platform/darwin/src/nsthread.mm b/platform/darwin/src/nsthread.mm
index 6caa1be43e..458db968d8 100644
--- a/platform/darwin/src/nsthread.mm
+++ b/platform/darwin/src/nsthread.mm
@@ -15,7 +15,8 @@ std::string getCurrentThreadName() {
}
void setCurrentThreadName(const std::string& name) {
- pthread_setname_np(name.c_str());
+ std::string qualifiedName = "com.mapbox.mbgl." + name;
+ pthread_setname_np(qualifiedName.c_str());
}
void makeThreadLowPriority() {
diff --git a/platform/darwin/src/run_loop.cpp b/platform/darwin/src/run_loop.cpp
index bae8164ab6..d60a88cf52 100644
--- a/platform/darwin/src/run_loop.cpp
+++ b/platform/darwin/src/run_loop.cpp
@@ -1,43 +1,39 @@
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/async_task.hpp>
-#include <mbgl/util/thread_local.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <CoreFoundation/CoreFoundation.h>
namespace mbgl {
namespace util {
-// Use a static function to avoid the static initialization order fiasco.
-static auto& current() {
- static ThreadLocal<RunLoop> tl;
- return tl;
-};
-
class RunLoop::Impl {
public:
std::unique_ptr<AsyncTask> async;
};
RunLoop* RunLoop::Get() {
- assert(current().get());
- return current().get();
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
}
RunLoop::RunLoop(Type)
: impl(std::make_unique<Impl>()) {
- assert(!current().get());
- current().set(this);
+ assert(!Scheduler::GetCurrent());
+ Scheduler::SetCurrent(this);
impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
}
RunLoop::~RunLoop() {
- assert(current().get());
- current().set(nullptr);
+ assert(Scheduler::GetCurrent());
+ Scheduler::SetCurrent(nullptr);
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
- withMutex([&] { queue.push(std::move(task)); });
- impl->async->send();
+ withMutex([&] {
+ queue.push(std::move(task));
+ impl->async->send();
+ });
}
void RunLoop::run() {
diff --git a/platform/darwin/test/MGLAttributionInfoTests.m b/platform/darwin/test/MGLAttributionInfoTests.m
index ed4927d44b..eccc6ceece 100644
--- a/platform/darwin/test/MGLAttributionInfoTests.m
+++ b/platform/darwin/test/MGLAttributionInfoTests.m
@@ -51,12 +51,12 @@
XCTAssertEqualObjects([infos[3] feedbackURLAtCenterCoordinate:mapbox zoomLevel:14],
[NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios#/77.63680/12.98108/14.00/0.0/0"]);
XCTAssertEqualObjects([infos[3] feedbackURLForStyleURL:styleURL atCenterCoordinate:mapbox zoomLevel:3.14159 direction:90.9 pitch:12.5],
- [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios&owner=mapbox&id=satellite-streets-v99&access_token#/77.63680/12.98108/3.14/90.9/13"]);
+ [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.sdk.ios&owner=mapbox&id=satellite-streets-v99&access_token&map_sdk_version=1.0.0#/77.63680/12.98108/3.14/90.9/13"]);
#else
XCTAssertEqualObjects([infos[3] feedbackURLAtCenterCoordinate:mapbox zoomLevel:14],
[NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL#/77.63680/12.98108/14.00/0.0/0"]);
XCTAssertEqualObjects([infos[3] feedbackURLForStyleURL:styleURL atCenterCoordinate:mapbox zoomLevel:3.14159 direction:90.9 pitch:12.5],
- [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL&owner=mapbox&id=satellite-streets-v99&access_token#/77.63680/12.98108/3.14/90.9/13"]);
+ [NSURL URLWithString:@"https://www.mapbox.com/feedback/?referrer=com.mapbox.MapboxGL&owner=mapbox&id=satellite-streets-v99&access_token&map_sdk_version=1.0.0#/77.63680/12.98108/3.14/90.9/13"]);
#endif
}
diff --git a/platform/darwin/test/MGLCircleStyleLayerTests.mm b/platform/darwin/test/MGLCircleStyleLayerTests.mm
index 2a2e9f2d4a..c0c503153a 100644
--- a/platform/darwin/test/MGLCircleStyleLayerTests.mm
+++ b/platform/darwin/test/MGLCircleStyleLayerTests.mm
@@ -246,6 +246,45 @@
XCTAssertEqual(circleOpacityTransition.duration, transitionTest.duration);
}
+ // circle-pitch-alignment
+ {
+ XCTAssertTrue(rawLayer->getCirclePitchAlignment().isUndefined(),
+ @"circle-pitch-alignment should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.circlePitchAlignment;
+
+ MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLCirclePitchAlignment:MGLCirclePitchAlignmentViewport]];
+ layer.circlePitchAlignment = constantStyleValue;
+ 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}} };
+ 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.");
+
+
+
+ layer.circlePitchAlignment = nil;
+ XCTAssertTrue(rawLayer->getCirclePitchAlignment().isUndefined(),
+ @"Unsetting circlePitchAlignment should return circle-pitch-alignment to the default value.");
+ XCTAssertEqualObjects(layer.circlePitchAlignment, defaultStyleValue,
+ @"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");
+ }
+
// circle-radius
{
XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(),
@@ -638,6 +677,7 @@
[self testPropertyName:@"circle-blur" isBoolean:NO];
[self testPropertyName:@"circle-color" isBoolean:NO];
[self testPropertyName:@"circle-opacity" isBoolean:NO];
+ [self testPropertyName:@"circle-pitch-alignment" isBoolean:NO];
[self testPropertyName:@"circle-radius" isBoolean:NO];
[self testPropertyName:@"circle-scale-alignment" isBoolean:NO];
[self testPropertyName:@"circle-stroke-color" isBoolean:NO];
@@ -648,6 +688,8 @@
}
- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLCirclePitchAlignment:MGLCirclePitchAlignmentMap].MGLCirclePitchAlignmentValue, MGLCirclePitchAlignmentMap);
+ XCTAssertEqual([NSValue valueWithMGLCirclePitchAlignment:MGLCirclePitchAlignmentViewport].MGLCirclePitchAlignmentValue, MGLCirclePitchAlignmentViewport);
XCTAssertEqual([NSValue valueWithMGLCircleScaleAlignment:MGLCircleScaleAlignmentMap].MGLCircleScaleAlignmentValue, MGLCircleScaleAlignmentMap);
XCTAssertEqual([NSValue valueWithMGLCircleScaleAlignment:MGLCircleScaleAlignmentViewport].MGLCircleScaleAlignmentValue, MGLCircleScaleAlignmentViewport);
XCTAssertEqual([NSValue valueWithMGLCircleTranslationAnchor:MGLCircleTranslationAnchorMap].MGLCircleTranslationAnchorValue, MGLCircleTranslationAnchorMap);
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 ae72b35d82..8762af9ba4 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -103,6 +103,18 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(mapView.style?.source(withIdentifier: "pois"))
}
+
+ func testMGLPolyline() {
+ //#-example-code
+ let coordinates = [
+ CLLocationCoordinate2D(latitude: 35.68476, longitude: -220.24257),
+ CLLocationCoordinate2D(latitude: 37.78428, longitude: -122.41310)
+ ]
+ let polyline = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count))
+ //#-end-example-code
+
+ XCTAssertNotNil(polyline)
+ }
func testMGLImageSource() {
//#-example-code
@@ -266,6 +278,24 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-end-example-code
}
+ func testMGLMapSnapshotter() {
+ //#-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))
+ options.zoomLevel = 10
+
+ let snapshotter = MGLMapSnapshotter(options: options)
+ snapshotter.start { (snapshot, error) in
+ if error != nil {
+ // error handler
+ } else {
+ // image handler
+ }
+ }
+ //#-end-example-code
+ }
+
// For testMGLMapView().
func myCustomFunction() {}
}
diff --git a/platform/darwin/test/MGLLightTest.mm b/platform/darwin/test/MGLLightTest.mm
index 2c3d1c7bd1..de64d57851 100644
--- a/platform/darwin/test/MGLLightTest.mm
+++ b/platform/darwin/test/MGLLightTest.mm
@@ -1,3 +1,5 @@
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
#import <XCTest/XCTest.h>
#import <Mapbox/Mapbox.h>
@@ -16,197 +18,112 @@
@implementation MGLLightTest
- (void)testProperties {
-
+
MGLTransition defaultTransition = MGLTransitionMake(0, 0);
MGLTransition transition = MGLTransitionMake(6, 3);
mbgl::style::TransitionOptions transitionOptions { { MGLDurationFromTimeInterval(6) }, { MGLDurationFromTimeInterval(3) } };
-
+
// anchor
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
-
- NSAssert([mglLight.anchor isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.anchor isn’t a MGLConstantStyleValue.");
+ 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(mglLight.anchorTransition.delay, defaultTransition.delay);
- XCTAssertEqual(mglLight.anchorTransition.duration, defaultTransition.duration);
-
- auto lightFromMGLlight = [mglLight mbglLight];
-
- XCTAssertEqual(light.getDefaultAnchor(), lightFromMGLlight.getAnchor().asConstant());
- auto anchorTransition = lightFromMGLlight.getAnchorTransition();
- XCTAssert(anchorTransition.delay && MGLTimeIntervalFromDuration(*anchorTransition.delay) == defaultTransition.delay);
- XCTAssert(anchorTransition.duration && MGLTimeIntervalFromDuration(*anchorTransition.duration) == defaultTransition.duration);
-
- MGLStyleValue<NSValue *> *anchorStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLightAnchor:MGLLightAnchorMap]];
- mglLight.anchor = anchorStyleValue;
- mglLight.anchorTransition = transition;
- NSAssert([mglLight.anchor isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.anchor isn’t a MGLConstantStyleValue.");
- anchorValue = ((MGLConstantStyleValue *)mglLight.anchor).rawValue;
-
- XCTAssertEqual(anchorValue.MGLLightAnchorValue, MGLLightAnchorMap);
- XCTAssertEqual(mglLight.anchorTransition.delay, transition.delay);
- XCTAssertEqual(mglLight.anchorTransition.duration, transition.duration);
-
- mbgl::style::PropertyValue<mbgl::style::LightAnchorType> anchorProperty = { mbgl::style::LightAnchorType::Map };
- light.setAnchor(anchorProperty);
- light.setAnchorTransition(transitionOptions);
-
+
+ mbgl::style::PropertyValue<mbgl::style::LightAnchorType> propertyValue = { mbgl::style::LightAnchorType::Viewport };
+ light.setAnchor(propertyValue);
+ mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
lightFromMGLlight = [mglLight mbglLight];
-
- XCTAssertEqual(light.getAnchor().asConstant(), lightFromMGLlight.getAnchor().asConstant());
- anchorTransition = lightFromMGLlight.getAnchorTransition();
- XCTAssert(anchorTransition.delay && MGLTimeIntervalFromDuration(*anchorTransition.delay) == transition.delay);
- XCTAssert(anchorTransition.duration && MGLTimeIntervalFromDuration(*anchorTransition.duration) == transition.duration);
-
+
+ XCTAssertEqual(light.getAnchor(), lightFromMGLlight.getAnchor());
}
-
+
// position
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- NSAssert([mglLight.position isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.position isn’t a MGLConstantStyleValue.");
- NSValue *positionValue = ((MGLConstantStyleValue *)mglLight.position).rawValue;
- auto positionArray = light.getDefaultPosition().getSpherical();
- MGLSphericalPosition defaultPosition = MGLSphericalPositionMake(positionArray[0], positionArray[1], positionArray[2]);
-
- XCTAssert(defaultPosition.radial == positionValue.MGLSphericalPositionValue.radial);
- XCTAssert(defaultPosition.azimuthal == positionValue.MGLSphericalPositionValue.azimuthal);
- XCTAssert(defaultPosition.polar == positionValue.MGLSphericalPositionValue.polar);
- XCTAssertEqual(mglLight.positionTransiton.delay, defaultTransition.delay);
- XCTAssertEqual(mglLight.positionTransiton.duration, defaultTransition.duration);
-
auto lightFromMGLlight = [mglLight mbglLight];
-
- XCTAssertEqual(positionArray, lightFromMGLlight.getPosition().asConstant().getSpherical());
+
+ 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);
-
- defaultPosition = MGLSphericalPositionMake(6, 180, 90);
- MGLStyleValue<NSValue *> *positionStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLSphericalPosition:defaultPosition]];
- mglLight.position = positionStyleValue;
- mglLight.positionTransiton = transition;
-
- NSAssert([mglLight.position isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.position isn’t a MGLConstantStyleValue.");
- positionValue = ((MGLConstantStyleValue *)mglLight.position).rawValue;
-
- XCTAssert(defaultPosition.radial == positionValue.MGLSphericalPositionValue.radial);
- XCTAssert(defaultPosition.azimuthal == positionValue.MGLSphericalPositionValue.azimuthal);
- XCTAssert(defaultPosition.polar == positionValue.MGLSphericalPositionValue.polar);
- XCTAssertEqual(mglLight.positionTransiton.delay, transition.delay);
- XCTAssertEqual(mglLight.positionTransiton.duration, transition.duration);
-
- lightFromMGLlight = [mglLight mbglLight];
-
- positionArray = { { 6, 180, 90 } };
+
+ std::array<float, 3> positionArray = { { 6, 180, 90 } };
mbgl::style::Position position = { positionArray };
- mbgl::style::PropertyValue<mbgl::style::Position> positionProperty = { position };
- light.setPosition(positionProperty);
+ mbgl::style::PropertyValue<mbgl::style::Position> propertyValue = { position };
+ light.setPosition(propertyValue);
light.setPositionTransition(transitionOptions);
- XCTAssertEqual(positionArray, lightFromMGLlight.getPosition().asConstant().getSpherical());
+ mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
+ lightFromMGLlight = [mglLight mbglLight];
+
+ XCTAssertEqual(light.getPosition(), lightFromMGLlight.getPosition());
positionTransition = lightFromMGLlight.getPositionTransition();
XCTAssert(positionTransition.delay && MGLTimeIntervalFromDuration(*positionTransition.delay) == transition.delay);
XCTAssert(positionTransition.duration && MGLTimeIntervalFromDuration(*positionTransition.duration) == transition.duration);
}
-
+
// color
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- NSAssert([mglLight.color isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.color isn’t a MGLConstantStyleValue.");
- MGLColor *colorValue = ((MGLConstantStyleValue *)mglLight.color).rawValue;
- auto color = light.getDefaultColor();
- const CGFloat *colorComponents = CGColorGetComponents(colorValue.CGColor);
-
- XCTAssert(color.r == colorComponents[0] && color.g == colorComponents[1] && color.b == colorComponents[2] &&
- color.a == colorComponents[3]);
- XCTAssertEqual(mglLight.colorTransiton.delay, defaultTransition.delay);
- XCTAssertEqual(mglLight.colorTransiton.duration, defaultTransition.duration);
-
auto lightFromMGLlight = [mglLight mbglLight];
-
- XCTAssertEqual(color, lightFromMGLlight.getColor().asConstant());
+
+ 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);
-
- MGLStyleValue<MGLColor *> *colorStyleValue = [MGLStyleValue<MGLColor *> valueWithRawValue:[MGLColor blackColor]];
- mglLight.color = colorStyleValue;
- mglLight.colorTransiton = transition;
-
- NSAssert([mglLight.color isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.color isn’t a MGLConstantStyleValue.");
- colorValue = ((MGLConstantStyleValue *)mglLight.color).rawValue;
-
- XCTAssertEqual([MGLColor blackColor], colorValue);
- XCTAssertEqual(mglLight.colorTransiton.delay, transition.delay);
- XCTAssertEqual(mglLight.colorTransiton.duration, transition.duration);
-
- mbgl::style::PropertyValue<mbgl::Color> colorProperty = { { 0, 0, 0, 1 } };
- light.setColor(colorProperty);
+
+ mbgl::style::PropertyValue<mbgl::Color> propertyValue = { { 1, 0, 0, 1 } };
+ light.setColor(propertyValue);
light.setColorTransition(transitionOptions);
-
+
+ mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
lightFromMGLlight = [mglLight mbglLight];
-
- colorComponents = CGColorGetComponents(colorValue.CGColor);
- color = lightFromMGLlight.getColor().asConstant();
- XCTAssertEqual(light.getColor().asConstant(),lightFromMGLlight.getColor().asConstant());
+
+ XCTAssertEqual(light.getColor(), lightFromMGLlight.getColor());
colorTransition = lightFromMGLlight.getColorTransition();
XCTAssert(colorTransition.delay && MGLTimeIntervalFromDuration(*colorTransition.delay) == transition.delay);
XCTAssert(colorTransition.duration && MGLTimeIntervalFromDuration(*colorTransition.duration) == transition.duration);
+
}
-
+
// intensity
{
mbgl::style::Light light;
MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
- NSAssert([mglLight.intensity isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.intensity isn’t a MGLConstantStyleValue.");
- NSNumber *intensityNumber = ((MGLConstantStyleValue *)mglLight.intensity).rawValue;
- auto intensity = light.getDefaultIntensity();
-
- XCTAssert(intensityNumber.floatValue == intensity);
- XCTAssertEqual(mglLight.intensityTransition.delay, defaultTransition.delay);
- XCTAssertEqual(mglLight.intensityTransition.duration, defaultTransition.duration);
-
auto lightFromMGLlight = [mglLight mbglLight];
-
- XCTAssertEqual(intensity, lightFromMGLlight.getIntensity().asConstant());
+
+ 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);
-
- NSNumber *intensityValue = @0.4;
- MGLStyleValue<NSNumber *> *intensityStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:intensityValue];
- mglLight.intensity = intensityStyleValue;
- mglLight.intensityTransition = transition;
-
- NSAssert([mglLight.intensity isKindOfClass:[MGLConstantStyleValue class]], @"mglLight.intensity isn’t a MGLConstantStyleValue.");
- intensityNumber = ((MGLConstantStyleValue *)mglLight.intensity).rawValue;
- XCTAssert(intensityNumber.floatValue == intensityValue.floatValue);
- XCTAssertEqual(mglLight.intensityTransition.delay, transition.delay);
- XCTAssertEqual(mglLight.intensityTransition.duration, transition.duration);
-
- mbgl::style::PropertyValue<float> intensityProperty = { 0.4 };
- light.setIntensity(intensityProperty);
+
+ mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ light.setIntensity(propertyValue);
light.setIntensityTransition(transitionOptions);
+ mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
lightFromMGLlight = [mglLight mbglLight];
-
- XCTAssertEqual(light.getIntensity().asConstant(), lightFromMGLlight.getIntensity().asConstant());
+
+ XCTAssertEqual(light.getIntensity(), lightFromMGLlight.getIntensity());
intensityTransition = lightFromMGLlight.getIntensityTransition();
XCTAssert(intensityTransition.delay && MGLTimeIntervalFromDuration(*intensityTransition.delay) == transition.delay);
XCTAssert(intensityTransition.duration && MGLTimeIntervalFromDuration(*intensityTransition.duration) == transition.duration);
-
+
}
}
- (void)testValueAdditions {
MGLSphericalPosition position = MGLSphericalPositionMake(1.15, 210, 30);
-
+
XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.radial, position.radial);
XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.azimuthal, position.azimuthal);
XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.polar, position.polar);
diff --git a/platform/darwin/test/MGLLightTest.mm.ejs b/platform/darwin/test/MGLLightTest.mm.ejs
new file mode 100644
index 0000000000..5b1f27d8d1
--- /dev/null
+++ b/platform/darwin/test/MGLLightTest.mm.ejs
@@ -0,0 +1,92 @@
+<%
+ const type = locals.type;
+ const properties = locals.properties;
+-%>
+// This file is generated.
+// Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+#import <XCTest/XCTest.h>
+#import <Mapbox/Mapbox.h>
+
+#import "MGLLight_Private.h"
+
+#import "../../darwin/src/NSDate+MGLAdditions.h"
+
+#import <mbgl/style/light.hpp>
+#import <mbgl/style/types.hpp>
+#include <mbgl/style/transition_options.hpp>
+
+@interface MGLLightTest : XCTestCase
+
+@end
+
+@implementation MGLLightTest
+
+- (void)testProperties {
+
+ MGLTransition defaultTransition = MGLTransitionMake(0, 0);
+ MGLTransition transition = MGLTransitionMake(6, 3);
+ mbgl::style::TransitionOptions transitionOptions { { MGLDurationFromTimeInterval(6) }, { MGLDurationFromTimeInterval(3) } };
+
+<% for (const property of properties) { -%>
+ // <%- property.name %>
+ {
+ mbgl::style::Light light;
+ MGLLight *mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
+ auto lightFromMGLlight = [mglLight mbglLight];
+
+ 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();
+ 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) -%>);
+
+<% } -%>
+<% if (property.type == "array") { -%>
+ std::array<float, 3> positionArray = { { 6, 180, 90 } };
+ mbgl::style::Position position = { positionArray };
+ mbgl::style::PropertyValue<mbgl::style::Position> propertyValue = { position };
+<% } else { -%>
+ mbgl::style::PropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> };
+<% } -%>
+ light.set<%- camelize(property.name) -%>(propertyValue);
+<% if (property.transition) { -%>
+ light.set<%- camelize(property.name) -%>Transition(transitionOptions);
+
+<% } -%>
+ mglLight = [[MGLLight alloc] initWithMBGLLight:&light];
+ lightFromMGLlight = [mglLight mbglLight];
+
+ XCTAssertEqual(light.get<%- camelize(property.name) -%>(), lightFromMGLlight.get<%- camelize(property.name) -%>());
+<% if (property.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);
+
+<% } -%>
+ }
+
+<% } -%>
+}
+
+- (void)testValueAdditions {
+ MGLSphericalPosition position = MGLSphericalPositionMake(1.15, 210, 30);
+
+ XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.radial, position.radial);
+ XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.azimuthal, position.azimuthal);
+ XCTAssertEqual([NSValue valueWithMGLSphericalPosition:position].MGLSphericalPositionValue.polar, position.polar);
+<% for (const property of properties) { -%>
+<% if (property.type == "enum") { -%>
+<% for (const value in property.values) { -%>
+ XCTAssertEqual([NSValue valueWithMGLLight<%- camelize(property.name) %>:MGLLight<%- camelize(property.name) %><%- camelize(value) %>].MGLLight<%- camelize(property.name) %>Value, MGLLight<%- camelize(property.name) %><%- camelize(value) %>);
+<% } -%>
+<% } -%>
+<% } -%>
+}
+
+@end
diff --git a/platform/darwin/test/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm
index be7d9a6754..7e7926e22e 100644
--- a/platform/darwin/test/MGLLineStyleLayerTests.mm
+++ b/platform/darwin/test/MGLLineStyleLayerTests.mm
@@ -95,7 +95,7 @@
MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLLineJoin:MGLLineJoinMiter]];
layer.lineJoin = constantStyleValue;
- mbgl::style::PropertyValue<mbgl::style::LineJoinType> propertyValue = { mbgl::style::LineJoinType::Miter };
+ 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,
@@ -119,11 +119,6 @@
@"Unsetting lineJoin should return line-join to the default value.");
XCTAssertEqualObjects(layer.lineJoin, defaultStyleValue,
@"lineJoin should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.lineJoin = 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.lineJoin = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// line-miter-limit
diff --git a/platform/darwin/test/MGLNSStringAdditionsTests.m b/platform/darwin/test/MGLNSStringAdditionsTests.m
index f07f2c1423..03503b7f8a 100644
--- a/platform/darwin/test/MGLNSStringAdditionsTests.m
+++ b/platform/darwin/test/MGLNSStringAdditionsTests.m
@@ -19,7 +19,7 @@
XCTAssertEqualObjects([@"Improve the map" mgl_titleCasedStringWithLocale:locale], @"Improve the Map");
XCTAssertEqualObjects([@"Improve The Map" mgl_titleCasedStringWithLocale:locale], @"Improve The Map");
-
+
XCTAssertEqualObjects([@"Improve a map" mgl_titleCasedStringWithLocale:locale], @"Improve a Map");
XCTAssertEqualObjects([@"Improve A Map" mgl_titleCasedStringWithLocale:locale], @"Improve A Map");
diff --git a/platform/darwin/test/MGLSDKTestHelpers.swift b/platform/darwin/test/MGLSDKTestHelpers.swift
index 82b5caa273..f21041782e 100644
--- a/platform/darwin/test/MGLSDKTestHelpers.swift
+++ b/platform/darwin/test/MGLSDKTestHelpers.swift
@@ -25,7 +25,8 @@ extension MGLSDKTestHelpers {
let methodDescriptionList: UnsafeMutablePointer<objc_method_description>! = protocol_copyMethodDescriptionList(p, false, true, &methodCount)
for i in 0..<Int(methodCount) {
let description: objc_method_description = methodDescriptionList[i]
- methods.insert(description.name.description)
+ XCTAssertNotNil(description.name?.description)
+ methods.insert(description.name!.description)
}
free(methodDescriptionList)
return methods
diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm
index 561af7f3d0..60500959b6 100644
--- a/platform/darwin/test/MGLShapeSourceTests.mm
+++ b/platform/darwin/test/MGLShapeSourceTests.mm
@@ -2,6 +2,7 @@
#import <Mapbox/Mapbox.h>
#import "MGLFeature_Private.h"
+#import "MGLAbstractShapeSource_Private.h"
#import "MGLShapeSource_Private.h"
#import "MGLSource_Private.h"
diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm
index 608cfdfd2d..8f610e338c 100644
--- a/platform/darwin/test/MGLStyleTests.mm
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -99,6 +99,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,
@@ -107,6 +109,7 @@
@(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,
"MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles.");
@@ -140,7 +143,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*;)RE");
+ NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*\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)];
diff --git a/platform/darwin/test/MGLStyleValueTests.swift b/platform/darwin/test/MGLStyleValueTests.swift
index 784b2fbaf1..c559037588 100644
--- a/platform/darwin/test/MGLStyleValueTests.swift
+++ b/platform/darwin/test/MGLStyleValueTests.swift
@@ -6,16 +6,34 @@ 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 actualComponents : [CGFloat] = [0, 0, 0, 0]
- var expectedComponents : [CGFloat] = [0, 0, 0, 0]
- actual.getRed(&(actualComponents[0]), green: &(actualComponents[1]), blue: &(actualComponents[2]), alpha: &(actualComponents[3]))
- expected.getRed(&(expectedComponents[0]), green: &(expectedComponents[1]), blue: &(expectedComponents[2]), alpha: &(expectedComponents[3]))
- for (ac, ec) in zip(actualComponents, expectedComponents) {
- XCTAssertEqualWithAccuracy(Float(ac), Float(ec), accuracy: accuracy)
- }
+ 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>) {
@@ -249,22 +267,50 @@ extension MGLStyleValueTests {
options: [.defaultValue: defaultRadius]
)
circleStyleLayer.circleRadius = expectedCompositeCategoricalValue
- XCTAssertEqual(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: smallRadius],
- 10: [200: smallRadius],
- 20: [200: largeRadius]
+ 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]
)
- circleStyleLayer.circleRadius = expectedCompositeExponentialValue
- XCTAssertEqual(circleStyleLayer.circleRadius, expectedCompositeExponentialValue)
+
+ 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> {
@@ -287,11 +333,30 @@ extension MGLStyleValueTests {
// data-driven, composite function with inner interval color stop values nested in outer camera stops
let expectedCompositeIntervalValue = MGLStyleValue<NSNumber>(
interpolationMode: .interval,
- compositeStops: radiusCompositeExponentialOrIntervalStops,
+ 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
)
- circleStyleLayer.circleRadius = expectedCompositeIntervalValue
- XCTAssertEqual(circleStyleLayer.circleRadius, expectedCompositeIntervalValue)
+
+ 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 367ebf363c..1ac86dd402 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -87,6 +87,40 @@
XCTAssertThrowsSpecificNamed(layer.iconAllowsOverlap = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
+ // icon-anchor
+ {
+ XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(),
+ @"icon-anchor should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconAnchor;
+
+ MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconAnchor:MGLIconAnchorBottomRight]];
+ layer.iconAnchor = constantStyleValue;
+ 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}} };
+ 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.");
+
+
+
+ layer.iconAnchor = nil;
+ XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(),
+ @"Unsetting iconAnchor should return icon-anchor to the default value.");
+ XCTAssertEqualObjects(layer.iconAnchor, defaultStyleValue,
+ @"iconAnchor should return the default value after being unset.");
+ }
+
// icon-ignore-placement
{
XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(),
@@ -301,6 +335,45 @@
XCTAssertThrowsSpecificNamed(layer.iconPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
+ // icon-pitch-alignment
+ {
+ XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(),
+ @"icon-pitch-alignment should be unset initially.");
+ MGLStyleValue<NSValue *> *defaultStyleValue = layer.iconPitchAlignment;
+
+ MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentAuto]];
+ layer.iconPitchAlignment = constantStyleValue;
+ 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}} };
+ 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.");
+
+
+
+ layer.iconPitchAlignment = nil;
+ XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(),
+ @"Unsetting iconPitchAlignment should return icon-pitch-alignment to the default value.");
+ XCTAssertEqualObjects(layer.iconPitchAlignment, defaultStyleValue,
+ @"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");
+ }
+
// icon-rotate
{
XCTAssertTrue(rawLayer->getIconRotate().isUndefined(),
@@ -663,7 +736,7 @@
MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
layer.maximumTextWidth = constantStyleValue;
- mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ 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,
@@ -680,6 +753,29 @@
XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue,
@"maximumTextWidth should round-trip camera functions.");
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
+ layer.maximumTextWidth = functionStyleValue;
+
+ 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.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
+ layer.maximumTextWidth = functionStyleValue;
+
+ 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->getTextMaxWidth(), propertyValue,
+ @"Setting maximumTextWidth to a composite function should update text-max-width.");
+ XCTAssertEqualObjects(layer.maximumTextWidth, functionStyleValue,
+ @"maximumTextWidth should round-trip composite functions.");
layer.maximumTextWidth = nil;
@@ -687,11 +783,6 @@
@"Unsetting maximumTextWidth should return text-max-width to the default value.");
XCTAssertEqualObjects(layer.maximumTextWidth, defaultStyleValue,
@"maximumTextWidth should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.maximumTextWidth = 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.maximumTextWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// symbol-avoid-edges
@@ -892,7 +983,7 @@
MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextAnchor:MGLTextAnchorBottomRight]];
layer.textAnchor = constantStyleValue;
- mbgl::style::PropertyValue<mbgl::style::TextAnchorType> propertyValue = { mbgl::style::TextAnchorType::BottomRight };
+ 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,
@@ -901,8 +992,8 @@
MGLStyleValue<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
layer.textAnchor = functionStyleValue;
- mbgl::style::IntervalStops<mbgl::style::TextAnchorType> intervalStops = { {{18, mbgl::style::TextAnchorType::BottomRight}} };
- propertyValue = mbgl::style::CameraFunction<mbgl::style::TextAnchorType> { intervalStops };
+ mbgl::style::IntervalStops<mbgl::style::SymbolAnchorType> intervalStops = { {{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.");
@@ -916,11 +1007,6 @@
@"Unsetting textAnchor should return text-anchor to the default value.");
XCTAssertEqualObjects(layer.textAnchor, defaultStyleValue,
@"textAnchor should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textAnchor = 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.textAnchor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// text-font
@@ -1066,7 +1152,7 @@
MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLTextJustification:MGLTextJustificationRight]];
layer.textJustification = constantStyleValue;
- mbgl::style::PropertyValue<mbgl::style::TextJustifyType> propertyValue = { mbgl::style::TextJustifyType::Right };
+ 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,
@@ -1090,11 +1176,6 @@
@"Unsetting textJustification should return text-justify to the default value.");
XCTAssertEqualObjects(layer.textJustification, defaultStyleValue,
@"textJustification should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textJustification = 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.textJustification = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// text-letter-spacing
@@ -1105,7 +1186,7 @@
MGLStyleValue<NSNumber *> *constantStyleValue = [MGLStyleValue<NSNumber *> valueWithRawValue:@0xff];
layer.textLetterSpacing = constantStyleValue;
- mbgl::style::PropertyValue<float> propertyValue = { 0xff };
+ 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,
@@ -1122,6 +1203,29 @@
XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue,
@"textLetterSpacing should round-trip camera functions.");
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil];
+ layer.textLetterSpacing = functionStyleValue;
+
+ 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.");
+
+ functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil];
+ layer.textLetterSpacing = functionStyleValue;
+
+ 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->getTextLetterSpacing(), propertyValue,
+ @"Setting textLetterSpacing to a composite function should update text-letter-spacing.");
+ XCTAssertEqualObjects(layer.textLetterSpacing, functionStyleValue,
+ @"textLetterSpacing should round-trip composite functions.");
layer.textLetterSpacing = nil;
@@ -1129,11 +1233,6 @@
@"Unsetting textLetterSpacing should return text-letter-spacing to the default value.");
XCTAssertEqualObjects(layer.textLetterSpacing, defaultStyleValue,
@"textLetterSpacing should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSNumber *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textLetterSpacing = 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.textLetterSpacing = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// text-line-height
@@ -2316,11 +2415,13 @@
- (void)testPropertyNames {
[self testPropertyName:@"icon-allows-overlap" isBoolean:YES];
+ [self testPropertyName:@"icon-anchor" isBoolean:NO];
[self testPropertyName:@"icon-ignores-placement" isBoolean:YES];
[self testPropertyName:@"icon-image-name" isBoolean:NO];
[self testPropertyName:@"icon-offset" isBoolean:NO];
[self testPropertyName:@"is-icon-optional" isBoolean:YES];
[self testPropertyName:@"icon-padding" isBoolean:NO];
+ [self testPropertyName:@"icon-pitch-alignment" isBoolean:NO];
[self testPropertyName:@"icon-rotation" isBoolean:NO];
[self testPropertyName:@"icon-rotation-alignment" isBoolean:NO];
[self testPropertyName:@"icon-scale" isBoolean:NO];
@@ -2366,6 +2467,18 @@
}
- (void)testValueAdditions {
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorCenter].MGLIconAnchorValue, MGLIconAnchorCenter);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorLeft].MGLIconAnchorValue, MGLIconAnchorLeft);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorRight].MGLIconAnchorValue, MGLIconAnchorRight);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorTop].MGLIconAnchorValue, MGLIconAnchorTop);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorBottom].MGLIconAnchorValue, MGLIconAnchorBottom);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorTopLeft].MGLIconAnchorValue, MGLIconAnchorTopLeft);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorTopRight].MGLIconAnchorValue, MGLIconAnchorTopRight);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorBottomLeft].MGLIconAnchorValue, MGLIconAnchorBottomLeft);
+ XCTAssertEqual([NSValue valueWithMGLIconAnchor:MGLIconAnchorBottomRight].MGLIconAnchorValue, MGLIconAnchorBottomRight);
+ XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentMap].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentMap);
+ XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentViewport].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentViewport);
+ XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentAuto].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentAuto);
XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentMap].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentMap);
XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentViewport].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentViewport);
XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentAuto].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentAuto);
diff --git a/platform/default/asset_file_source.cpp b/platform/default/asset_file_source.cpp
index 343f3b70ce..54dbb8d0f6 100644
--- a/platform/default/asset_file_source.cpp
+++ b/platform/default/asset_file_source.cpp
@@ -9,7 +9,6 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <unistd.h>
namespace mbgl {
@@ -35,7 +34,7 @@ public:
struct stat buf;
int result = stat(path.c_str(), &buf);
- if (result == 0 && S_ISDIR(buf.st_mode)) {
+ if (result == 0 && (S_IFDIR & buf.st_mode)) {
response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound);
} else if (result == -1 && errno == ENOENT) {
response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound);
diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp
index bf8d7b6348..608b782ab9 100644
--- a/platform/default/default_file_source.cpp
+++ b/platform/default/default_file_source.cpp
@@ -28,10 +28,15 @@ namespace mbgl {
class DefaultFileSource::Impl {
public:
- Impl(ActorRef<Impl>, std::shared_ptr<FileSource> assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize)
+ Impl(ActorRef<Impl> self, std::shared_ptr<FileSource> assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize)
: assetFileSource(assetFileSource_)
- , localFileSource(std::make_unique<LocalFileSource>())
- , offlineDatabase(cachePath, maximumCacheSize) {
+ , localFileSource(std::make_unique<LocalFileSource>()) {
+ // Initialize the Database asynchronously so as to not block Actor creation.
+ self.invoke(&Impl::initializeOfflineDatabase, cachePath, maximumCacheSize);
+ }
+
+ void initializeOfflineDatabase(std::string cachePath, uint64_t maximumCacheSize) {
+ offlineDatabase = std::make_unique<OfflineDatabase>(cachePath, maximumCacheSize);
}
void setAPIBaseURL(const std::string& url) {
@@ -56,7 +61,7 @@ public:
void listRegions(std::function<void (std::exception_ptr, optional<std::vector<OfflineRegion>>)> callback) {
try {
- callback({}, offlineDatabase.listRegions());
+ callback({}, offlineDatabase->listRegions());
} catch (...) {
callback(std::current_exception(), {});
}
@@ -66,7 +71,7 @@ public:
const OfflineRegionMetadata& metadata,
std::function<void (std::exception_ptr, optional<OfflineRegion>)> callback) {
try {
- callback({}, offlineDatabase.createRegion(definition, metadata));
+ callback({}, offlineDatabase->createRegion(definition, metadata));
} catch (...) {
callback(std::current_exception(), {});
}
@@ -76,7 +81,7 @@ public:
const OfflineRegionMetadata& metadata,
std::function<void (std::exception_ptr, optional<OfflineRegionMetadata>)> callback) {
try {
- callback({}, offlineDatabase.updateMetadata(regionID, metadata));
+ callback({}, offlineDatabase->updateMetadata(regionID, metadata));
} catch (...) {
callback(std::current_exception(), {});
}
@@ -93,7 +98,7 @@ public:
void deleteRegion(OfflineRegion&& region, std::function<void (std::exception_ptr)> callback) {
try {
downloads.erase(region.getID());
- offlineDatabase.deleteRegion(std::move(region));
+ offlineDatabase->deleteRegion(std::move(region));
callback({});
} catch (...) {
callback(std::current_exception());
@@ -121,33 +126,43 @@ public:
tasks[req] = localFileSource->request(resource, callback);
} else {
// Try the offline database
- Resource revalidation = resource;
-
- const bool hasPrior = resource.priorEtag || resource.priorModified || resource.priorExpires;
- if (!hasPrior || resource.necessity == Resource::Optional) {
- auto offlineResponse = offlineDatabase.get(resource);
-
- if (resource.necessity == Resource::Optional && !offlineResponse) {
- // Ensure there's always a response that we can send, so the caller knows that
- // there's no optional data available in the cache.
- offlineResponse.emplace();
- offlineResponse->noContent = true;
- offlineResponse->error = std::make_unique<Response::Error>(
- Response::Error::Reason::NotFound, "Not found in offline database");
- }
-
- if (offlineResponse) {
- revalidation.priorModified = offlineResponse->modified;
- revalidation.priorExpires = offlineResponse->expires;
- revalidation.priorEtag = offlineResponse->etag;
+ if (resource.hasLoadingMethod(Resource::LoadingMethod::Cache)) {
+ auto offlineResponse = offlineDatabase->get(resource);
+
+ if (resource.loadingMethod == Resource::LoadingMethod::CacheOnly) {
+ if (!offlineResponse) {
+ // Ensure there's always a response that we can send, so the caller knows that
+ // there's no optional data available in the cache, when it's the only place
+ // we're supposed to load from.
+ offlineResponse.emplace();
+ offlineResponse->noContent = true;
+ offlineResponse->error = std::make_unique<Response::Error>(
+ Response::Error::Reason::NotFound, "Not found in offline database");
+ } else if (!offlineResponse->isUsable()) {
+ // Don't return resources the server requested not to show when they're stale.
+ // Even if we can't directly use the response, we may still use it to send a
+ // conditional HTTP request, which is why we're saving it above.
+ offlineResponse->error = std::make_unique<Response::Error>(
+ Response::Error::Reason::NotFound, "Cached resource is unusable");
+ }
callback(*offlineResponse);
+ } else if (offlineResponse) {
+ // Copy over the fields so that we can use them when making a refresh request.
+ resource.priorModified = offlineResponse->modified;
+ resource.priorExpires = offlineResponse->expires;
+ resource.priorEtag = offlineResponse->etag;
+ resource.priorData = offlineResponse->data;
+
+ if (offlineResponse->isUsable()) {
+ callback(*offlineResponse);
+ }
}
}
// Get from the online file source
- if (resource.necessity == Resource::Required) {
- tasks[req] = onlineFileSource.request(revalidation, [=] (Response onlineResponse) mutable {
- this->offlineDatabase.put(revalidation, onlineResponse);
+ if (resource.hasLoadingMethod(Resource::LoadingMethod::Network)) {
+ tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) mutable {
+ this->offlineDatabase->put(resource, onlineResponse);
callback(onlineResponse);
});
}
@@ -159,11 +174,15 @@ public:
}
void setOfflineMapboxTileCountLimit(uint64_t limit) {
- offlineDatabase.setOfflineMapboxTileCountLimit(limit);
+ offlineDatabase->setOfflineMapboxTileCountLimit(limit);
+ }
+
+ void setOnlineStatus(const bool status) {
+ onlineFileSource.setOnlineStatus(status);
}
void put(const Resource& resource, const Response& response) {
- offlineDatabase.put(resource, response);
+ offlineDatabase->put(resource, response);
}
private:
@@ -173,13 +192,13 @@ private:
return *it->second;
}
return *downloads.emplace(regionID,
- std::make_unique<OfflineDownload>(regionID, offlineDatabase.getRegionDefinition(regionID), offlineDatabase, onlineFileSource)).first->second;
+ std::make_unique<OfflineDownload>(regionID, offlineDatabase->getRegionDefinition(regionID), *offlineDatabase, onlineFileSource)).first->second;
}
// shared so that destruction is done on the creating thread
const std::shared_ptr<FileSource> assetFileSource;
const std::unique_ptr<FileSource> localFileSource;
- OfflineDatabase offlineDatabase;
+ std::unique_ptr<OfflineDatabase> offlineDatabase;
OnlineFileSource onlineFileSource;
std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks;
std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads;
@@ -288,6 +307,10 @@ void DefaultFileSource::resume() {
// For testing only:
+void DefaultFileSource::setOnlineStatus(const bool status) {
+ impl->actor().invoke(&Impl::setOnlineStatus, status);
+}
+
void DefaultFileSource::put(const Resource& resource, const Response& response) {
impl->actor().invoke(&Impl::put, resource, response);
}
diff --git a/src/mbgl/storage/file_source_request.cpp b/platform/default/file_source_request.cpp
index 8a6fb21181..09ea8cc32a 100644
--- a/src/mbgl/storage/file_source_request.cpp
+++ b/platform/default/file_source_request.cpp
@@ -1,13 +1,13 @@
#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/actor/mailbox.hpp>
-#include <mbgl/util/run_loop.hpp>
+#include <mbgl/actor/scheduler.hpp>
namespace mbgl {
FileSourceRequest::FileSourceRequest(FileSource::Callback&& callback)
: responseCallback(callback)
- , mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())) {
+ , mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())) {
}
FileSourceRequest::~FileSourceRequest() {
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/http_file_source.cpp b/platform/default/http_file_source.cpp
index 867d85fa4d..a9c442c2de 100644
--- a/platform/default/http_file_source.cpp
+++ b/platform/default/http_file_source.cpp
@@ -325,7 +325,9 @@ size_t HTTPRequest::headerCallback(char *const buffer, const size_t size, const
baton->response->etag = std::string(buffer + begin, length - begin - 2); // remove \r\n
} else if ((begin = headerMatches("cache-control: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
- baton->response->expires = http::CacheControl::parse(value.c_str()).toTimePoint();
+ const auto cc = http::CacheControl::parse(value.c_str());
+ baton->response->expires = cc.toTimePoint();
+ baton->response->mustRevalidate = cc.mustRevalidate;
} else if ((begin = headerMatches("expires: ", buffer, length)) != std::string::npos) {
const std::string value { buffer + begin, length - begin - 2 }; // remove \r\n
baton->response->expires = Timestamp{ Seconds(curl_getdate(value.c_str(), nullptr)) };
diff --git a/platform/default/local_glyph_rasterizer.cpp b/platform/default/local_glyph_rasterizer.cpp
new file mode 100644
index 0000000000..7866f29420
--- /dev/null
+++ b/platform/default/local_glyph_rasterizer.cpp
@@ -0,0 +1,22 @@
+#include <mbgl/text/local_glyph_rasterizer.hpp>
+
+namespace mbgl {
+
+class LocalGlyphRasterizer::Impl {
+};
+
+LocalGlyphRasterizer::LocalGlyphRasterizer(const optional<std::string>)
+{}
+
+LocalGlyphRasterizer::~LocalGlyphRasterizer()
+{}
+
+bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID) {
+ return false;
+}
+
+Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack&, GlyphID) {
+ return Glyph();
+}
+
+} // namespace mbgl
diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp
index df3a517823..ba08aecab7 100644
--- a/platform/default/mbgl/gl/headless_backend.cpp
+++ b/platform/default/mbgl/gl/headless_backend.cpp
@@ -1,7 +1,6 @@
#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/headless_display.hpp>
#include <mbgl/gl/context.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <cassert>
#include <stdexcept>
@@ -9,43 +8,78 @@
namespace mbgl {
-HeadlessBackend::HeadlessBackend() = default;
+class HeadlessBackend::View {
+public:
+ View(gl::Context& context, Size size_)
+ : color(context.createRenderbuffer<gl::RenderbufferType::RGBA>(size_)),
+ depthStencil(context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size_)),
+ framebuffer(context.createFramebuffer(color, depthStencil)) {
+ }
+
+ gl::Renderbuffer<gl::RenderbufferType::RGBA> color;
+ gl::Renderbuffer<gl::RenderbufferType::DepthStencil> depthStencil;
+ gl::Framebuffer framebuffer;
+};
-HeadlessBackend::HeadlessBackend(std::shared_ptr<HeadlessDisplay> display_)
- : display(std::move(display_)) {
+HeadlessBackend::HeadlessBackend(Size size_)
+ : size(size_) {
}
HeadlessBackend::~HeadlessBackend() {
- BackendScope scope(*this);
+ BackendScope guard { *this };
+ view.reset();
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;
}
+void HeadlessBackend::bind() {
+ gl::Context& context_ = getContext();
+
+ if (!view) {
+ view = std::make_unique<View>(context_, size);
+ }
+
+ context_.bindFramebuffer = view->framebuffer.framebuffer;
+ context_.scissorTest = false;
+ context_.viewport = { 0, 0, size };
+}
+
+Size HeadlessBackend::getFramebufferSize() const {
+ return size;
+}
+
void HeadlessBackend::updateAssumedState() {
// no-op
}
-void HeadlessBackend::invalidate() {
- assert(false);
+void HeadlessBackend::setSize(Size size_) {
+ size = size_;
+ view.reset();
+}
+
+PremultipliedImage HeadlessBackend::readStillImage() {
+ return getContext().readFramebuffer<PremultipliedImage>(size);
}
} // namespace mbgl
diff --git a/platform/default/mbgl/gl/headless_backend.hpp b/platform/default/mbgl/gl/headless_backend.hpp
index 26033acf82..7757037533 100644
--- a/platform/default/mbgl/gl/headless_backend.hpp
+++ b/platform/default/mbgl/gl/headless_backend.hpp
@@ -1,46 +1,50 @@
#pragma once
-#include <mbgl/map/backend.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
#include <memory>
#include <functional>
namespace mbgl {
-class HeadlessDisplay;
-
-class HeadlessBackend : public Backend {
+class HeadlessBackend : public RendererBackend {
public:
- HeadlessBackend();
- HeadlessBackend(std::shared_ptr<HeadlessDisplay>);
+ HeadlessBackend(Size = { 256, 256 });
~HeadlessBackend() override;
+ void bind() override;
+ Size getFramebufferSize() const override;
void updateAssumedState() override;
- void invalidate() override;
+ 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;
+ float pixelRatio;
bool active = false;
+
+ class View;
+ std::unique_ptr<View> view;
};
} // namespace mbgl
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 a5c95085b8..0000000000
--- a/platform/default/mbgl/gl/headless_display.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include <memory>
-
-namespace mbgl {
-
-class HeadlessDisplay {
-public:
- HeadlessDisplay();
- ~HeadlessDisplay();
-
- template <typename DisplayAttribute>
- DisplayAttribute attribute() const;
-
-private:
- 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
new file mode 100644
index 0000000000..4263d2b148
--- /dev/null
+++ b/platform/default/mbgl/gl/headless_frontend.cpp
@@ -0,0 +1,96 @@
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+namespace mbgl {
+
+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, GLContextMode mode, const optional<std::string> localFontFamily)
+ : size(size_),
+ pixelRatio(pixelRatio_),
+ backend({ static_cast<uint32_t>(size.width * pixelRatio),
+ static_cast<uint32_t>(size.height * pixelRatio) }),
+ asyncInvalidate([this] {
+ if (renderer && updateParameters) {
+ mbgl::BackendScope guard { backend };
+ renderer->render(*updateParameters);
+ }
+ }),
+ renderer(std::make_unique<Renderer>(backend, pixelRatio, fileSource, scheduler, mode, programCacheDir, localFontFamily)) {
+}
+
+HeadlessFrontend::~HeadlessFrontend() = default;
+
+void HeadlessFrontend::reset() {
+ assert(renderer);
+ renderer.reset();
+}
+
+void HeadlessFrontend::update(std::shared_ptr<UpdateParameters> updateParameters_) {
+ updateParameters = updateParameters_;
+ asyncInvalidate.send();
+}
+
+void HeadlessFrontend::setObserver(RendererObserver& observer_) {
+ assert(renderer);
+ renderer->setObserver(&observer_);
+}
+
+Size HeadlessFrontend::getSize() const {
+ return size;
+}
+
+Renderer* HeadlessFrontend::getRenderer() {
+ assert(renderer);
+ return renderer.get();
+}
+
+RendererBackend* HeadlessFrontend::getBackend() {
+ return &backend;
+}
+
+void HeadlessFrontend::setSize(Size size_) {
+ if (size != size_) {
+ size = size_;
+ backend.setSize({ static_cast<uint32_t>(size_.width * pixelRatio),
+ static_cast<uint32_t>(size_.height * pixelRatio) });
+ }
+}
+
+PremultipliedImage HeadlessFrontend::readStillImage() {
+ return backend.readStillImage();
+}
+
+PremultipliedImage HeadlessFrontend::render(Map& map) {
+ PremultipliedImage result;
+
+ map.renderStill([&](std::exception_ptr error) {
+ if (error) {
+ std::rethrow_exception(error);
+ } else {
+ result = backend.readStillImage();
+ }
+ });
+
+ while (!result.valid()) {
+ util::RunLoop::Get()->runOnce();
+ }
+
+ return result;
+}
+
+optional<TransformState> HeadlessFrontend::getTransformState() const {
+ if (updateParameters) {
+ return updateParameters->transformState;
+ } else {
+ return {};
+ }
+}
+
+} // namespace mbgl
diff --git a/platform/default/mbgl/gl/headless_frontend.hpp b/platform/default/mbgl/gl/headless_frontend.hpp
new file mode 100644
index 0000000000..8ae617d37b
--- /dev/null
+++ b/platform/default/mbgl/gl/headless_frontend.hpp
@@ -0,0 +1,52 @@
+#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>
+#include <mbgl/util/optional.hpp>
+
+#include <memory>
+
+namespace mbgl {
+
+class FileSource;
+class Scheduler;
+class Renderer;
+class RendererBackend;
+class Map;
+class TransformState;
+
+class HeadlessFrontend : public RendererFrontend {
+public:
+ 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;
+ void update(std::shared_ptr<UpdateParameters>) override;
+ void setObserver(RendererObserver&) override;
+
+ Size getSize() const;
+ void setSize(Size);
+
+ Renderer* getRenderer();
+ RendererBackend* getBackend();
+
+ PremultipliedImage readStillImage();
+ PremultipliedImage render(Map&);
+
+ optional<TransformState> getTransformState() const;
+
+private:
+ Size size;
+ float pixelRatio;
+
+ HeadlessBackend backend;
+ util::AsyncTask asyncInvalidate;
+
+ std::unique_ptr<Renderer> renderer;
+ std::shared_ptr<UpdateParameters> updateParameters;
+};
+
+} // namespace mbgl
diff --git a/platform/default/mbgl/gl/offscreen_view.cpp b/platform/default/mbgl/gl/offscreen_view.cpp
deleted file mode 100644
index e7cf7cffe5..0000000000
--- a/platform/default/mbgl/gl/offscreen_view.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include <mbgl/gl/offscreen_view.hpp>
-#include <mbgl/gl/context.hpp>
-#include <mbgl/gl/framebuffer.hpp>
-#include <mbgl/gl/renderbuffer.hpp>
-#include <mbgl/util/optional.hpp>
-
-#include <cstring>
-#include <cassert>
-
-namespace mbgl {
-
-class OffscreenView::Impl {
-public:
- Impl(gl::Context& context_, const Size size_) : context(context_), size(std::move(size_)) {
- assert(!size.isEmpty());
- }
-
- void bind() {
- if (!framebuffer) {
- color = context.createRenderbuffer<gl::RenderbufferType::RGBA>(size);
- depthStencil = context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size);
- framebuffer = context.createFramebuffer(*color, *depthStencil);
- } else {
- context.bindFramebuffer = framebuffer->framebuffer;
- }
-
- context.scissorTest = false;
- context.viewport = { 0, 0, size };
- }
-
- PremultipliedImage readStillImage() {
- return context.readFramebuffer<PremultipliedImage>(size);
- }
-
- const Size& getSize() const {
- return size;
- }
-
-private:
- gl::Context& context;
- const Size size;
- optional<gl::Framebuffer> framebuffer;
- optional<gl::Renderbuffer<gl::RenderbufferType::RGBA>> color;
- optional<gl::Renderbuffer<gl::RenderbufferType::DepthStencil>> depthStencil;
-};
-
-OffscreenView::OffscreenView(gl::Context& context, const Size size)
- : impl(std::make_unique<Impl>(context, std::move(size))) {
-}
-
-OffscreenView::~OffscreenView() = default;
-
-void OffscreenView::bind() {
- impl->bind();
-}
-
-PremultipliedImage OffscreenView::readStillImage() {
- return impl->readStillImage();
-}
-
-const Size& OffscreenView::getSize() const {
- return impl->getSize();
-}
-
-} // namespace mbgl
diff --git a/platform/default/mbgl/gl/offscreen_view.hpp b/platform/default/mbgl/gl/offscreen_view.hpp
deleted file mode 100644
index eb888272e5..0000000000
--- a/platform/default/mbgl/gl/offscreen_view.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <mbgl/map/view.hpp>
-#include <mbgl/util/image.hpp>
-
-namespace mbgl {
-
-namespace gl {
-class Context;
-} // namespace gl
-
-class OffscreenView : public View {
-public:
- OffscreenView(gl::Context&, Size size = { 256, 256 });
- ~OffscreenView() override;
-
- void bind() override;
-
- PremultipliedImage readStillImage();
-
- const Size& getSize() const;
-
-private:
- class Impl;
- const std::unique_ptr<Impl> impl;
-};
-
-} // namespace mbgl
diff --git a/platform/default/mbgl/map/map_snapshotter.cpp b/platform/default/mbgl/map/map_snapshotter.cpp
new file mode 100644
index 0000000000..9341c23cfd
--- /dev/null
+++ b/platform/default/mbgl/map/map_snapshotter.cpp
@@ -0,0 +1,184 @@
+#include <mbgl/map/map_snapshotter.hpp>
+
+#include <mbgl/actor/actor_ref.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/event.hpp>
+#include <mbgl/map/transform.hpp>
+
+namespace mbgl {
+
+class MapSnapshotter::Impl {
+public:
+ Impl(FileSource&,
+ Scheduler&,
+ const std::string& styleURL,
+ const Size&,
+ const float pixelRatio,
+ const CameraOptions&,
+ const optional<LatLngBounds> region,
+ const optional<std::string> programCacheDir);
+
+ void setStyleURL(std::string styleURL);
+ std::string getStyleURL() const;
+
+ void setSize(Size);
+ Size getSize() const;
+
+ void setCameraOptions(CameraOptions);
+ CameraOptions getCameraOptions() const;
+
+ void setRegion(LatLngBounds);
+ LatLngBounds getRegion() const;
+
+ void snapshot(ActorRef<MapSnapshotter::Callback>);
+
+private:
+ HeadlessFrontend frontend;
+ Map map;
+};
+
+MapSnapshotter::Impl::Impl(FileSource& fileSource,
+ Scheduler& scheduler,
+ const std::string& styleURL,
+ const Size& size,
+ const float pixelRatio,
+ const CameraOptions& cameraOptions,
+ const optional<LatLngBounds> region,
+ const optional<std::string> programCacheDir)
+ : frontend(size, pixelRatio, fileSource, scheduler, programCacheDir)
+ , map(frontend, MapObserver::nullObserver(), size, pixelRatio, fileSource, scheduler, MapMode::Static) {
+
+ map.getStyle().loadURL(styleURL);
+
+ map.jumpTo(cameraOptions);
+
+ // Set region, if specified
+ if (region) {
+ this->setRegion(*region);
+ }
+}
+
+void MapSnapshotter::Impl::snapshot(ActorRef<MapSnapshotter::Callback> callback) {
+ map.renderStill([this, callback = std::move(callback)] (std::exception_ptr error) mutable {
+
+ // Create lambda that captures the current transform state
+ // and can be used to translate for geographic to screen
+ // coordinates
+ assert (frontend.getTransformState());
+ PointForFn pointForFn { [=, center=map.getLatLng(), transformState = *frontend.getTransformState()] (const LatLng& latLng) {
+ LatLng unwrappedLatLng = latLng.wrapped();
+ unwrappedLatLng.unwrapForShortestPath(center);
+ Transform transform { transformState };
+ return transform.latLngToScreenCoordinate(unwrappedLatLng);
+ }};
+
+ // Collect all source attributions
+ std::vector<std::string> attributions;
+ for (auto source : map.getStyle().getSources()) {
+ auto attribution = source->getAttribution();
+ if (attribution) {
+ attributions.push_back(*attribution);
+ }
+ }
+
+ // Invoke callback
+ callback.invoke(
+ &MapSnapshotter::Callback::operator(),
+ error,
+ error ? PremultipliedImage() : frontend.readStillImage(),
+ std::move(attributions),
+ std::move(pointForFn)
+ );
+ });
+}
+
+void MapSnapshotter::Impl::setStyleURL(std::string styleURL) {
+ map.getStyle().loadURL(styleURL);
+}
+
+std::string MapSnapshotter::Impl::getStyleURL() const {
+ return map.getStyle().getURL();
+}
+
+void MapSnapshotter::Impl::setSize(Size size) {
+ map.setSize(size);
+ frontend.setSize(size);
+}
+
+Size MapSnapshotter::Impl::getSize() const {
+ return map.getSize();
+}
+
+void MapSnapshotter::Impl::setCameraOptions(CameraOptions cameraOptions) {
+ map.jumpTo(cameraOptions);
+}
+
+CameraOptions MapSnapshotter::Impl::getCameraOptions() const {
+ EdgeInsets insets;
+ return map.getCameraOptions(insets);
+}
+
+void MapSnapshotter::Impl::setRegion(LatLngBounds region) {
+ mbgl::EdgeInsets insets = { 0, 0, 0, 0 };
+ std::vector<LatLng> latLngs = { region.southwest(), region.northeast() };
+ map.jumpTo(map.cameraForLatLngs(latLngs, insets));
+}
+
+LatLngBounds MapSnapshotter::Impl::getRegion() const {
+ return map.latLngBoundsForCamera(getCameraOptions());
+}
+
+MapSnapshotter::MapSnapshotter(FileSource& fileSource,
+ Scheduler& scheduler,
+ const std::string& styleURL,
+ const Size& size,
+ const float pixelRatio,
+ const CameraOptions& cameraOptions,
+ const optional<LatLngBounds> region,
+ const optional<std::string> programCacheDir)
+ : impl(std::make_unique<util::Thread<MapSnapshotter::Impl>>("Map Snapshotter", fileSource, scheduler, styleURL, size, pixelRatio, cameraOptions, region, programCacheDir)) {
+}
+
+MapSnapshotter::~MapSnapshotter() = default;
+
+void MapSnapshotter::snapshot(ActorRef<MapSnapshotter::Callback> callback) {
+ impl->actor().invoke(&Impl::snapshot, std::move(callback));
+}
+
+void MapSnapshotter::setStyleURL(const std::string& styleURL) {
+ impl->actor().invoke(&Impl::setStyleURL, styleURL);
+}
+
+std::string MapSnapshotter::getStyleURL() const {
+ return impl->actor().ask(&Impl::getStyleURL).get();
+}
+
+void MapSnapshotter::setSize(const Size& size) {
+ impl->actor().invoke(&Impl::setSize, size);
+}
+
+Size MapSnapshotter::getSize() const {
+ return impl->actor().ask(&Impl::getSize).get();
+}
+
+void MapSnapshotter::setCameraOptions(const CameraOptions& options) {
+ impl->actor().invoke(&Impl::setCameraOptions, options);
+}
+
+CameraOptions MapSnapshotter::getCameraOptions() const {
+ return impl->actor().ask(&Impl::getCameraOptions).get();
+}
+
+void MapSnapshotter::setRegion(const LatLngBounds& bounds) {
+ impl->actor().invoke(&Impl::setRegion, std::move(bounds));
+}
+
+LatLngBounds MapSnapshotter::getRegion() const {
+ return impl->actor().ask(&Impl::getRegion).get();
+}
+
+} // namespace mbgl
diff --git a/platform/default/mbgl/map/map_snapshotter.hpp b/platform/default/mbgl/map/map_snapshotter.hpp
new file mode 100644
index 0000000000..985396e5a3
--- /dev/null
+++ b/platform/default/mbgl/map/map_snapshotter.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/thread.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/geo.hpp>
+
+#include <exception>
+#include <memory>
+#include <string>
+#include <vector>
+#include <functional>
+
+namespace mbgl {
+
+template<class> class ActorRef;
+struct CameraOptions;
+class FileSource;
+class Size;
+class LatLngBounds;
+
+namespace style {
+class Style;
+} // namespace style
+
+class MapSnapshotter {
+public:
+ MapSnapshotter(FileSource& fileSource,
+ Scheduler& scheduler,
+ const std::string& styleURL,
+ const Size&,
+ const float pixelRatio,
+ const CameraOptions&,
+ const optional<LatLngBounds> region,
+ const optional<std::string> cacheDir = {});
+
+ ~MapSnapshotter();
+
+ void setStyleURL(const std::string& styleURL);
+ std::string getStyleURL() const;
+
+ void setSize(const Size&);
+ Size getSize() const;
+
+ void setCameraOptions(const CameraOptions&);
+ CameraOptions getCameraOptions() const;
+
+ void setRegion(const LatLngBounds&);
+ LatLngBounds getRegion() const;
+
+ using PointForFn = std::function<ScreenCoordinate (const LatLng&)>;
+ using Attributions = std::vector<std::string>;
+ using Callback = std::function<void (std::exception_ptr, PremultipliedImage, Attributions, PointForFn)>;
+ void snapshot(ActorRef<Callback>);
+
+private:
+ class Impl;
+ std::unique_ptr<util::Thread<Impl>> impl;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/storage/file_source_request.hpp b/platform/default/mbgl/storage/file_source_request.hpp
index 6bd0d44df6..6bd0d44df6 100644
--- a/src/mbgl/storage/file_source_request.hpp
+++ b/platform/default/mbgl/storage/file_source_request.hpp
diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp
index fd2d47819b..7670790be9 100644
--- a/platform/default/mbgl/storage/offline.cpp
+++ b/platform/default/mbgl/storage/offline.cpp
@@ -1,6 +1,7 @@
#include <mbgl/storage/offline.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/tileset.hpp>
+#include <mbgl/util/projection.hpp>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
@@ -23,18 +24,12 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition(
}
}
-std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(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);
-
- assert(minZ >= 0);
- assert(maxZ >= 0);
- assert(minZ < std::numeric_limits<uint8_t>::max());
- assert(maxZ < std::numeric_limits<uint8_t>::max());
+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;
- for (uint8_t z = minZ; z <= maxZ; z++) {
+ for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
for (const auto& tile : util::tileCover(bounds, z)) {
result.emplace_back(tile.canonical);
}
@@ -43,6 +38,28 @@ std::vector<CanonicalTileID> OfflineTilePyramidRegionDefinition::tileCover(Sourc
return result;
}
+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);
+ }
+
+ return result;
+}
+
+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);
+
+ assert(minZ >= 0);
+ assert(maxZ >= 0);
+ assert(minZ < std::numeric_limits<uint8_t>::max());
+ assert(maxZ < std::numeric_limits<uint8_t>::max());
+ return { static_cast<uint8_t>(minZ), static_cast<uint8_t>(maxZ) };
+}
+
OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) {
rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
doc.Parse<0>(region.c_str());
diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp
index 02736f10a4..65c2097182 100644
--- a/platform/default/mbgl/storage/offline_database.cpp
+++ b/platform/default/mbgl/storage/offline_database.cpp
@@ -49,8 +49,9 @@ void OfflineDatabase::ensureSchema() {
case 2: migrateToVersion3(); // fall through
case 3: // no-op and fall through
case 4: migrateToVersion5(); // fall through
- case 5: return;
- default: throw std::runtime_error("unknown schema version");
+ case 5: migrateToVersion6(); // fall through
+ case 6: return;
+ default: break; // downgrade, delete the database
}
removeExisting();
@@ -83,7 +84,7 @@ void OfflineDatabase::ensureSchema() {
db->exec("PRAGMA journal_mode = DELETE");
db->exec("PRAGMA synchronous = FULL");
db->exec(schema);
- db->exec("PRAGMA user_version = 5");
+ db->exec("PRAGMA user_version = 6");
} catch (...) {
Log::Error(Event::Database, "Unexpected error creating database schema: %s", util::toString(std::current_exception()).c_str());
throw;
@@ -126,6 +127,14 @@ void OfflineDatabase::migrateToVersion5() {
db->exec("PRAGMA user_version = 5");
}
+void OfflineDatabase::migrateToVersion6() {
+ mapbox::sqlite::Transaction transaction(*db);
+ db->exec("ALTER TABLE resources ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0");
+ db->exec("ALTER TABLE tiles ADD COLUMN must_revalidate INTEGER NOT NULL DEFAULT 0");
+ db->exec("PRAGMA user_version = 6");
+ transaction.commit();
+}
+
OfflineDatabase::Statement OfflineDatabase::getStatement(const char * sql) {
auto it = statements.find(sql);
@@ -188,11 +197,11 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource,
if (resource.kind == Resource::Kind::Tile) {
assert(resource.tileData);
inserted = putTile(*resource.tileData, response,
- compressed ? compressedData : *response.data,
+ compressed ? compressedData : response.data ? *response.data : "",
compressed);
} else {
inserted = putResource(resource, response,
- compressed ? compressedData : *response.data,
+ compressed ? compressedData : response.data ? *response.data : "",
compressed);
}
@@ -211,8 +220,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
// clang-format off
Statement stmt = getStatement(
- // 0 1 2 3 4
- "SELECT etag, expires, modified, data, compressed "
+ // 0 1 2 3 4 5
+ "SELECT etag, expires, must_revalidate, modified, data, compressed "
"FROM resources "
"WHERE url = ?");
// clang-format on
@@ -226,14 +235,15 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
Response response;
uint64_t size = 0;
- response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<Timestamp>>(1);
- response.modified = stmt->get<optional<Timestamp>>(2);
+ 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);
- optional<std::string> data = stmt->get<optional<std::string>>(3);
+ optional<std::string> data = stmt->get<optional<std::string>>(4);
if (!data) {
response.noContent = true;
- } else if (stmt->get<int>(4)) {
+ } else if (stmt->get<bool>(5)) {
response.data = std::make_shared<std::string>(util::decompress(*data));
size = data->length();
} else {
@@ -265,14 +275,16 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement update = getStatement(
"UPDATE resources "
- "SET accessed = ?1, "
- " expires = ?2 "
- "WHERE url = ?3 ");
+ "SET accessed = ?1, "
+ " expires = ?2, "
+ " must_revalidate = ?3 "
+ "WHERE url = ?4 ");
// clang-format on
update->bind(1, util::now());
update->bind(2, response.expires);
- update->bind(3, resource.url);
+ update->bind(3, response.mustRevalidate);
+ update->bind(4, resource.url);
update->run();
return false;
}
@@ -286,29 +298,31 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement update = getStatement(
"UPDATE resources "
- "SET kind = ?1, "
- " etag = ?2, "
- " expires = ?3, "
- " modified = ?4, "
- " accessed = ?5, "
- " data = ?6, "
- " compressed = ?7 "
- "WHERE url = ?8 ");
+ "SET kind = ?1, "
+ " etag = ?2, "
+ " expires = ?3, "
+ " must_revalidate = ?4, "
+ " modified = ?5, "
+ " accessed = ?6, "
+ " data = ?7, "
+ " compressed = ?8 "
+ "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.modified);
- update->bind(5, util::now());
- update->bind(8, resource.url);
+ update->bind(4, response.mustRevalidate);
+ update->bind(5, response.modified);
+ update->bind(6, util::now());
+ update->bind(9, resource.url);
if (response.noContent) {
- update->bind(6, nullptr);
- update->bind(7, false);
+ update->bind(7, nullptr);
+ update->bind(8, false);
} else {
- update->bindBlob(6, data.data(), data.size(), false);
- update->bind(7, compressed);
+ update->bindBlob(7, data.data(), data.size(), false);
+ update->bind(8, compressed);
}
update->run();
@@ -319,23 +333,24 @@ bool OfflineDatabase::putResource(const Resource& resource,
// clang-format off
Statement insert = getStatement(
- "INSERT INTO resources (url, kind, etag, expires, modified, accessed, data, compressed) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8) ");
+ "INSERT INTO resources (url, kind, etag, expires, must_revalidate, modified, accessed, data, compressed) "
+ "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.modified);
- insert->bind(6, util::now());
+ insert->bind(5, response.mustRevalidate);
+ insert->bind(6, response.modified);
+ insert->bind(7, util::now());
if (response.noContent) {
- insert->bind(7, nullptr);
- insert->bind(8, false);
+ insert->bind(8, nullptr);
+ insert->bind(9, false);
} else {
- insert->bindBlob(7, data.data(), data.size(), false);
- insert->bind(8, compressed);
+ insert->bindBlob(8, data.data(), data.size(), false);
+ insert->bind(9, compressed);
}
insert->run();
@@ -366,8 +381,8 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
// clang-format off
Statement stmt = getStatement(
- // 0 1 2 3 4
- "SELECT etag, expires, modified, data, compressed "
+ // 0 1 2, 3, 4, 5
+ "SELECT etag, expires, must_revalidate, modified, data, compressed "
"FROM tiles "
"WHERE url_template = ?1 "
" AND pixel_ratio = ?2 "
@@ -389,14 +404,15 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
Response response;
uint64_t size = 0;
- response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<Timestamp>>(1);
- response.modified = stmt->get<optional<Timestamp>>(2);
+ 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);
- optional<std::string> data = stmt->get<optional<std::string>>(3);
+ optional<std::string> data = stmt->get<optional<std::string>>(4);
if (!data) {
response.noContent = true;
- } else if (stmt->get<int>(4)) {
+ } else if (stmt->get<bool>(5)) {
response.data = std::make_shared<std::string>(util::decompress(*data));
size = data->length();
} else {
@@ -440,22 +456,24 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement update = getStatement(
"UPDATE tiles "
- "SET accessed = ?1, "
- " expires = ?2 "
- "WHERE url_template = ?3 "
- " AND pixel_ratio = ?4 "
- " AND x = ?5 "
- " AND y = ?6 "
- " AND z = ?7 ");
+ "SET accessed = ?1, "
+ " expires = ?2, "
+ " must_revalidate = ?3 "
+ "WHERE url_template = ?4 "
+ " AND pixel_ratio = ?5 "
+ " AND x = ?6 "
+ " AND y = ?7 "
+ " AND z = ?8 ");
// clang-format on
update->bind(1, util::now());
update->bind(2, response.expires);
- update->bind(3, tile.urlTemplate);
- update->bind(4, tile.pixelRatio);
- update->bind(5, tile.x);
- update->bind(6, tile.y);
- update->bind(7, tile.z);
+ 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();
return false;
}
@@ -469,35 +487,37 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement update = getStatement(
"UPDATE tiles "
- "SET modified = ?1, "
- " etag = ?2, "
- " expires = ?3, "
- " accessed = ?4, "
- " data = ?5, "
- " compressed = ?6 "
- "WHERE url_template = ?7 "
- " AND pixel_ratio = ?8 "
- " AND x = ?9 "
- " AND y = ?10 "
- " AND z = ?11 ");
+ "SET modified = ?1, "
+ " etag = ?2, "
+ " expires = ?3, "
+ " must_revalidate = ?4, "
+ " accessed = ?5, "
+ " data = ?6, "
+ " compressed = ?7 "
+ "WHERE url_template = ?8 "
+ " AND pixel_ratio = ?9 "
+ " AND x = ?10 "
+ " AND y = ?11 "
+ " AND z = ?12 ");
// clang-format on
update->bind(1, response.modified);
update->bind(2, response.etag);
update->bind(3, response.expires);
- update->bind(4, util::now());
- update->bind(7, tile.urlTemplate);
- update->bind(8, tile.pixelRatio);
- update->bind(9, tile.x);
- update->bind(10, tile.y);
- update->bind(11, tile.z);
+ 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);
if (response.noContent) {
- update->bind(5, nullptr);
- update->bind(6, false);
+ update->bind(6, nullptr);
+ update->bind(7, false);
} else {
- update->bindBlob(5, data.data(), data.size(), false);
- update->bind(6, compressed);
+ update->bindBlob(6, data.data(), data.size(), false);
+ update->bind(7, compressed);
}
update->run();
@@ -508,8 +528,8 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
// clang-format off
Statement insert = getStatement(
- "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, etag, expires, accessed, data, compressed) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11) ");
+ "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)");
// clang-format on
insert->bind(1, tile.urlTemplate);
@@ -518,16 +538,17 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
insert->bind(4, tile.y);
insert->bind(5, tile.z);
insert->bind(6, response.modified);
- insert->bind(7, response.etag);
- insert->bind(8, response.expires);
- insert->bind(9, util::now());
+ insert->bind(7, response.mustRevalidate);
+ insert->bind(8, response.etag);
+ insert->bind(9, response.expires);
+ insert->bind(10, util::now());
if (response.noContent) {
- insert->bind(10, nullptr);
- insert->bind(11, false);
+ insert->bind(11, nullptr);
+ insert->bind(12, false);
} else {
- insert->bindBlob(10, data.data(), data.size(), false);
- insert->bind(11, compressed);
+ insert->bindBlob(11, data.data(), data.size(), false);
+ insert->bind(12, compressed);
}
insert->run();
diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp
index 57ffcee4eb..91b544a9e0 100644
--- a/platform/default/mbgl/storage/offline_database.hpp
+++ b/platform/default/mbgl/storage/offline_database.hpp
@@ -64,6 +64,7 @@ private:
void removeExisting();
void migrateToVersion3();
void migrateToVersion5();
+ void migrateToVersion6();
class Statement {
public:
diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp
index 7f0001f64b..8bb16993a5 100644
--- a/platform/default/mbgl/storage/offline_download.cpp
+++ b/platform/default/mbgl/storage/offline_download.cpp
@@ -80,7 +80,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
auto handleTiledSource = [&] (const variant<std::string, Tileset>& urlOrTileset, const uint16_t tileSize) {
if (urlOrTileset.is<Tileset>()) {
result.requiredResourceCount +=
- definition.tileCover(type, tileSize, urlOrTileset.get<Tileset>().zoomRange).size();
+ definition.tileCount(type, tileSize, urlOrTileset.get<Tileset>().zoomRange);
} else {
result.requiredResourceCount += 1;
const auto& url = urlOrTileset.get<std::string>();
@@ -90,7 +90,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
optional<Tileset> tileset = style::conversion::convertJSON<Tileset>(*sourceResponse->data, error);
if (tileset) {
result.requiredResourceCount +=
- definition.tileCover(type, tileSize, (*tileset).zoomRange).size();
+ definition.tileCount(type, tileSize, (*tileset).zoomRange);
}
} else {
result.requiredResourceCountIsPrecise = false;
@@ -129,6 +129,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const {
case SourceType::Video:
case SourceType::Annotations:
+ case SourceType::CustomVector:
break;
}
}
@@ -214,6 +215,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/storage/offline_schema.cpp.include b/platform/default/mbgl/storage/offline_schema.cpp.include
index a80c7677e6..41af81e55b 100644
--- a/platform/default/mbgl/storage/offline_schema.cpp.include
+++ b/platform/default/mbgl/storage/offline_schema.cpp.include
@@ -10,6 +10,7 @@ static const char * schema =
" data BLOB,\n"
" compressed INTEGER NOT NULL DEFAULT 0,\n"
" accessed INTEGER NOT NULL,\n"
+" must_revalidate INTEGER NOT NULL DEFAULT 0,\n"
" UNIQUE (url)\n"
");\n"
"CREATE TABLE tiles (\n"
@@ -25,6 +26,7 @@ static const char * schema =
" data BLOB,\n"
" compressed INTEGER NOT NULL DEFAULT 0,\n"
" accessed INTEGER NOT NULL,\n"
+" must_revalidate INTEGER NOT NULL DEFAULT 0,\n"
" UNIQUE (url_template, pixel_ratio, z, x, y)\n"
");\n"
"CREATE TABLE regions (\n"
diff --git a/platform/default/mbgl/storage/offline_schema.sql b/platform/default/mbgl/storage/offline_schema.sql
index 9df8fa6a89..722b0e0451 100644
--- a/platform/default/mbgl/storage/offline_schema.sql
+++ b/platform/default/mbgl/storage/offline_schema.sql
@@ -8,6 +8,7 @@ CREATE TABLE resources ( -- Generic table for style, source, s
data BLOB,
compressed INTEGER NOT NULL DEFAULT 0,
accessed INTEGER NOT NULL,
+ must_revalidate INTEGER NOT NULL DEFAULT 0,
UNIQUE (url)
);
@@ -24,6 +25,7 @@ CREATE TABLE tiles (
data BLOB,
compressed INTEGER NOT NULL DEFAULT 0,
accessed INTEGER NOT NULL,
+ must_revalidate INTEGER NOT NULL DEFAULT 0,
UNIQUE (url_template, pixel_ratio, z, x, y)
);
diff --git a/platform/default/mbgl/util/default_styles.cpp b/platform/default/mbgl/util/default_styles.cpp
deleted file mode 100644
index 5f4ca862fe..0000000000
--- a/platform/default/mbgl/util/default_styles.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <mbgl/util/default_styles.hpp>
-
-namespace mbgl {
-namespace util {
-namespace default_styles {
-
-const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v10", "Streets", 10 };
-const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v10", "Outdoors", 10 };
-const DefaultStyle light = { "mapbox://styles/mapbox/light-v9", "Light", 9 };
-const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark", 9 };
-const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 };
-const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets", 10 };
-const DefaultStyle trafficDay = { "mapbox://styles/mapbox/traffic-day-v2", "Traffic Day", 2 };
-const DefaultStyle trafficNight = { "mapbox://styles/mapbox/traffic-night-v2", "Traffic Night", 2 };
-
-} // namespace default_styles
-} // end namespace util
-} // end namespace mbgl
diff --git a/platform/default/mbgl/util/default_styles.hpp b/platform/default/mbgl/util/default_styles.hpp
index 466102d623..43dafb8083 100644
--- a/platform/default/mbgl/util/default_styles.hpp
+++ b/platform/default/mbgl/util/default_styles.hpp
@@ -13,14 +13,14 @@ struct DefaultStyle {
const unsigned currentVersion;
};
-extern const DefaultStyle streets;
-extern const DefaultStyle outdoors;
-extern const DefaultStyle light;
-extern const DefaultStyle dark;
-extern const DefaultStyle satellite;
-extern const DefaultStyle satelliteStreets;
-extern const DefaultStyle trafficDay;
-extern const DefaultStyle trafficNight;
+constexpr const DefaultStyle streets = { "mapbox://styles/mapbox/streets-v10", "Streets", 10 };
+constexpr const DefaultStyle outdoors = { "mapbox://styles/mapbox/outdoors-v10", "Outdoors", 10 };
+constexpr const DefaultStyle light = { "mapbox://styles/mapbox/light-v9", "Light", 9 };
+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,
diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp
index f10e0f8ffb..d685109b95 100644
--- a/platform/default/online_file_source.cpp
+++ b/platform/default/online_file_source.cpp
@@ -117,13 +117,24 @@ public:
}
void activateRequest(OnlineFileRequest* request) {
- activeRequests.insert(request);
- request->request = httpFileSource.request(request->resource, [=] (Response response) {
+ auto callback = [=](Response response) {
activeRequests.erase(request);
- activatePendingRequest();
request->request.reset();
request->completed(response);
- });
+ activatePendingRequest();
+ };
+
+ activeRequests.insert(request);
+
+ if (online) {
+ request->request = httpFileSource.request(request->resource, callback);
+ } else {
+ Response response;
+ response.error = std::make_unique<Response::Error>(Response::Error::Reason::Connection,
+ "Online connectivity is disabled.");
+ callback(response);
+ }
+
assert(pendingRequestsMap.size() == pendingRequestsList.size());
}
@@ -153,6 +164,11 @@ public:
resourceTransform = std::move(transform);
}
+ void setOnlineStatus(const bool status) {
+ online = status;
+ networkIsReachableAgain();
+ }
+
private:
void networkIsReachableAgain() {
for (auto& request : allRequests) {
@@ -178,6 +194,7 @@ private:
std::unordered_map<OnlineFileRequest*, std::list<OnlineFileRequest*>::iterator> pendingRequestsMap;
std::unordered_set<OnlineFileRequest*> activeRequests;
+ bool online = true;
HTTPFileSource httpFileSource;
util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) };
};
@@ -320,6 +337,14 @@ void OnlineFileRequest::completed(Response response) {
resource.priorModified = response.modified;
}
+ if (response.notModified && resource.priorData) {
+ // When the priorData field is set, it indicates that we had to revalidate the request and
+ // that the requestor hasn't gotten data yet. If we get a 304 response, this means that we
+ // have send the cached data to give the requestor a chance to actually obtain the data.
+ response.data = std::move(resource.priorData);
+ response.notModified = false;
+ }
+
bool isExpired = false;
if (response.expires) {
@@ -375,10 +400,16 @@ ActorRef<OnlineFileRequest> OnlineFileRequest::actor() {
if (!mailbox) {
// Lazy constructed because this can be costly and
// the ResourceTransform is not used by many apps.
- mailbox = std::make_shared<Mailbox>(*util::RunLoop::Get());
+ mailbox = std::make_shared<Mailbox>(*Scheduler::GetCurrent());
}
return ActorRef<OnlineFileRequest>(*this, mailbox);
}
+// For testing only:
+
+void OnlineFileSource::setOnlineStatus(const bool status) {
+ impl->setOnlineStatus(status);
+}
+
} // namespace mbgl
diff --git a/platform/default/resources/default_marker.svg b/platform/default/resources/default_marker.svg
index 3cb5af09a4..08ae2d76d6 100644
--- a/platform/default/resources/default_marker.svg
+++ b/platform/default/resources/default_marker.svg
@@ -4,7 +4,7 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="20px" height="56px" viewBox="0 0 20 56" enable-background="new 0 0 20 56" xml:space="preserve">
<ellipse opacity="0.2" fill="#262626" cx="10" cy="27" rx="9" ry="5"/>
-<path id="path5359" fill="#D84E4E" stroke="#7F1919" stroke-width="1.0229" stroke-linecap="round" stroke-linejoin="round" d="
+<path id="path5359" fill="#F84D4D" stroke="#951212" stroke-width="1.0229" stroke-linecap="round" stroke-linejoin="round" d="
M19.5,10.4c0,6.3-9.5,17.1-9.5,17.1S0.5,16.6,0.5,10.4c0-5.5,4.3-9.9,9.5-9.9S19.5,4.9,19.5,10.4z"/>
<circle id="path5990" fill="#FFFFFF" stroke="#7C2525" stroke-linecap="round" stroke-linejoin="round" cx="10" cy="10" r="3.8"/>
</svg>
diff --git a/platform/default/run_loop.cpp b/platform/default/run_loop.cpp
index 98d1badcb5..5bccd21d56 100644
--- a/platform/default/run_loop.cpp
+++ b/platform/default/run_loop.cpp
@@ -1,6 +1,7 @@
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/async_task.hpp>
#include <mbgl/util/thread_local.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <uv.h>
@@ -10,9 +11,6 @@
namespace {
-using namespace mbgl::util;
-static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
-
void dummyCallback(uv_async_t*) {}
} // namespace
@@ -53,7 +51,8 @@ struct Watch {
};
RunLoop* RunLoop::Get() {
- return current.get();
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
}
class RunLoop::Impl {
@@ -98,12 +97,12 @@ RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) {
impl->type = type;
- current.set(this);
+ Scheduler::SetCurrent(this);
impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
}
RunLoop::~RunLoop() {
- current.set(nullptr);
+ Scheduler::SetCurrent(nullptr);
// Close the dummy handle that we have
// just to keep the main loop alive.
@@ -127,12 +126,14 @@ RunLoop::~RunLoop() {
}
LOOP_HANDLE RunLoop::getLoopHandle() {
- return current.get()->impl->loop;
+ return Get()->impl->loop;
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
- withMutex([&] { queue.push(std::move(task)); });
- impl->async->send();
+ withMutex([&] {
+ queue.push(std::move(task));
+ impl->async->send();
+ });
}
void RunLoop::run() {
diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp
index 0707f9255c..2e08354fdf 100644
--- a/platform/default/sqlite3.cpp
+++ b/platform/default/sqlite3.cpp
@@ -287,6 +287,11 @@ bool Statement::run() {
}
}
+template <> bool Statement::get(int offset) {
+ assert(impl);
+ return sqlite3_column_int(impl->stmt, offset);
+}
+
template <> int Statement::get(int offset) {
assert(impl);
return sqlite3_column_int(impl->stmt, offset);
@@ -381,8 +386,8 @@ int64_t Statement::lastInsertRowId() const {
uint64_t Statement::changes() const {
assert(impl);
- auto changes = impl->changes;
- return (changes < 0 ? 0 : changes);
+ auto changes_ = impl->changes;
+ return (changes_ < 0 ? 0 : changes_);
}
Transaction::Transaction(Database& db_, Mode mode)
diff --git a/platform/default/thread_local.cpp b/platform/default/thread_local.cpp
new file mode 100644
index 0000000000..db70773c12
--- /dev/null
+++ b/platform/default/thread_local.cpp
@@ -0,0 +1,66 @@
+#include <mbgl/util/thread_local.hpp>
+
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <stdexcept>
+#include <cassert>
+
+#include <pthread.h>
+
+namespace mbgl {
+namespace util {
+
+template <class T>
+class ThreadLocal<T>::Impl {
+public:
+ pthread_key_t key;
+};
+
+template <class T>
+ThreadLocal<T>::ThreadLocal() : impl(std::make_unique<Impl>()) {
+ int ret = pthread_key_create(&impl->key, [](void *) {});
+
+ if (ret) {
+ throw std::runtime_error("Failed to init local storage key.");
+ }
+}
+
+template <class T>
+ThreadLocal<T>::~ThreadLocal() {
+ // ThreadLocal will not take ownership
+ // of the pointer it is managing. The pointer
+ // needs to be explicitly cleared before we
+ // destroy this object.
+ assert(!get());
+
+ if (pthread_key_delete(impl->key)) {
+ Log::Error(Event::General, "Failed to delete local storage key.");
+ assert(false);
+ }
+}
+
+template <class T>
+T* ThreadLocal<T>::get() {
+ auto* ret = reinterpret_cast<T*>(pthread_getspecific(impl->key));
+ if (!ret) {
+ return nullptr;
+ }
+
+ return ret;
+}
+
+template <class T>
+void ThreadLocal<T>::set(T* ptr) {
+ if (pthread_setspecific(impl->key, ptr)) {
+ throw std::runtime_error("Failed to set local storage.");
+ }
+}
+
+template class ThreadLocal<BackendScope>;
+template class ThreadLocal<Scheduler>;
+template class ThreadLocal<int>; // For unit tests
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/default/utf.cpp b/platform/default/utf.cpp
index ba9678c91f..8fc44a9ed3 100644
--- a/platform/default/utf.cpp
+++ b/platform/default/utf.cpp
@@ -2,14 +2,24 @@
#include <memory>
#include <locale>
+
+// GCC 4.9 compatibility
+#if !defined(__GNUC__) || __GNUC__ >= 5
#include <codecvt>
+#else
+#include <boost/locale/encoding_utf.hpp>
+#endif
namespace mbgl {
namespace util {
std::u16string utf8_to_utf16::convert(std::string const& utf8) {
+#if !defined(__GNUC__) || __GNUC__ >= 5
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
return converter.from_bytes(utf8);
+#else
+ return boost::locale::conv::utf_to_utf<char16_t>(utf8.c_str(), utf8.c_str() + utf8.size());
+#endif
}
} // namespace util
diff --git a/platform/glfw/glfw_renderer_frontend.cpp b/platform/glfw/glfw_renderer_frontend.cpp
new file mode 100644
index 0000000000..73205f1c56
--- /dev/null
+++ b/platform/glfw/glfw_renderer_frontend.cpp
@@ -0,0 +1,41 @@
+#include "glfw_renderer_frontend.hpp"
+
+#include <mbgl/renderer/renderer.hpp>
+
+GLFWRendererFrontend::GLFWRendererFrontend(std::unique_ptr<mbgl::Renderer> renderer_, GLFWView& glfwView_)
+ : glfwView(glfwView_)
+ , renderer(std::move(renderer_)) {
+ glfwView.setRenderFrontend(this);
+}
+
+GLFWRendererFrontend::~GLFWRendererFrontend() = default;
+
+void GLFWRendererFrontend::reset() {
+ assert(renderer);
+ renderer.reset();
+}
+
+void GLFWRendererFrontend::setObserver(mbgl::RendererObserver& observer) {
+ assert(renderer);
+ renderer->setObserver(&observer);
+}
+
+void GLFWRendererFrontend::update(std::shared_ptr<mbgl::UpdateParameters> params) {
+ updateParameters = std::move(params);
+ glfwView.invalidate();
+}
+
+void GLFWRendererFrontend::render() {
+ assert(renderer);
+
+ if (!updateParameters) return;
+
+ mbgl::BackendScope guard { glfwView, mbgl::BackendScope::ScopeType::Implicit };
+
+ renderer->render(*updateParameters);
+}
+
+mbgl::Renderer* GLFWRendererFrontend::getRenderer() {
+ assert(renderer);
+ return renderer.get();
+}
diff --git a/platform/glfw/glfw_renderer_frontend.hpp b/platform/glfw/glfw_renderer_frontend.hpp
new file mode 100644
index 0000000000..c992fe20fe
--- /dev/null
+++ b/platform/glfw/glfw_renderer_frontend.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "glfw_view.hpp"
+#include <mbgl/renderer/renderer_frontend.hpp>
+
+#include <memory>
+
+namespace mbgl {
+class Renderer;
+} // namespace mbgl
+
+class GLFWRendererFrontend : public mbgl::RendererFrontend {
+public:
+ GLFWRendererFrontend(std::unique_ptr<mbgl::Renderer>, GLFWView&);
+ ~GLFWRendererFrontend() override;
+
+ void reset() override;
+ void setObserver(mbgl::RendererObserver&) override;
+
+ void update(std::shared_ptr<mbgl::UpdateParameters>) override;
+ void render();
+
+ mbgl::Renderer* getRenderer();
+
+private:
+ GLFWView& glfwView;
+ std::unique_ptr<mbgl::Renderer> renderer;
+ std::shared_ptr<mbgl::UpdateParameters> updateParameters;
+};
diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp
index 1beaf2b52b..8fc2fba283 100644
--- a/platform/glfw/glfw_view.cpp
+++ b/platform/glfw/glfw_view.cpp
@@ -1,4 +1,5 @@
#include "glfw_view.hpp"
+#include "glfw_renderer_frontend.hpp"
#include "ny_route.hpp"
#include <mbgl/annotation/annotation.hpp>
@@ -10,7 +11,8 @@
#include <mbgl/util/platform.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/chrono.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/map/camera.hpp>
#include <mapbox/cheap_ruler.hpp>
@@ -108,6 +110,7 @@ GLFWView::GLFWView(bool fullscreen_, bool benchmark_)
printf("- Press `N` to reset north\n");
printf("- Press `R` to enable the route demo\n");
printf("- Press `E` to insert an example building extrusion layer\n");
+ printf("- Press `O` to toggle online connectivity\n");
printf("- Press `Z` to cycle through north orientations\n");
printf("- Prezz `X` to cycle through the viewport modes\n");
printf("- Press `A` to cycle through Mapbox offices in the world + dateline monument\n");
@@ -142,6 +145,10 @@ void GLFWView::setMap(mbgl::Map *map_) {
map->addAnnotationImage(makeImage("default_marker", 22, 22, 1));
}
+void GLFWView::setRenderFrontend(GLFWRendererFrontend* rendererFrontend_) {
+ rendererFrontend = rendererFrontend_;
+}
+
void GLFWView::updateAssumedState() {
assumeFramebufferBinding(0);
assumeViewport(0, 0, getFramebufferSize());
@@ -170,6 +177,9 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
if (!mods)
view->map->resetPosition();
break;
+ case GLFW_KEY_O:
+ view->onlineStatusCallback();
+ break;
case GLFW_KEY_S:
if (view->changeStyleCallback)
view->changeStyleCallback();
@@ -196,7 +206,7 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action,
view->nextOrientation();
break;
case GLFW_KEY_Q: {
- auto result = view->map->queryPointAnnotations({ {}, { double(view->getSize().width), double(view->getSize().height) } });
+ auto result = view->rendererFrontend->getRenderer()->queryPointAnnotations({ {}, { double(view->getSize().width), double(view->getSize().height) } });
printf("visible point annotations: %lu\n", result.size());
} break;
case GLFW_KEY_P:
@@ -489,16 +499,16 @@ void GLFWView::run() {
glfwPollEvents();
- if (dirty) {
+ if (dirty && rendererFrontend) {
+ dirty = false;
const double started = glfwGetTime();
if (animateRouteCallback)
animateRouteCallback(map);
activate();
- mbgl::BackendScope scope { *this, mbgl::BackendScope::ScopeType::Implicit };
- map->render(*this);
+ rendererFrontend->render();
glfwSwapBuffers(window);
@@ -507,7 +517,6 @@ void GLFWView::run() {
invalidate();
}
- dirty = false;
}
};
@@ -531,7 +540,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 366fe4fd68..35f17b723a 100644
--- a/platform/glfw/glfw_view.hpp
+++ b/platform/glfw/glfw_view.hpp
@@ -1,15 +1,15 @@
#pragma once
#include <mbgl/map/map.hpp>
-#include <mbgl/map/view.hpp>
-#include <mbgl/map/backend.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/timer.hpp>
#include <mbgl/util/geometry.hpp>
struct GLFWwindow;
+class GLFWRendererFrontend;
-class GLFWView : public mbgl::View, public mbgl::Backend {
+class GLFWView : public mbgl::RendererBackend, public mbgl::MapObserver {
public:
GLFWView(bool fullscreen = false, bool benchmark = false);
~GLFWView() override;
@@ -17,6 +17,8 @@ public:
float getPixelRatio() const;
void setMap(mbgl::Map*);
+
+ void setRenderFrontend(GLFWRendererFrontend*);
// Callback called when the user presses the key mapped to style change.
// The expected action is to set a new style, different to the current one.
@@ -26,19 +28,23 @@ public:
pauseResumeCallback = callback;
};
+ void setOnlineStatusCallback(std::function<void()> callback) {
+ onlineStatusCallback = callback;
+ }
+
void setShouldClose();
void setWindowTitle(const std::string&);
void run();
+
+ void invalidate();
- // mbgl::View implementation
- void bind() override;
mbgl::Size getSize() const;
- mbgl::Size getFramebufferSize() const;
+ mbgl::Size getFramebufferSize() const override;
- // mbgl::Backend implementation
- void invalidate() override;
+ // mbgl::RendererBackend implementation
+ void bind() override;
void updateAssumedState() override;
// mbgl::MapObserver implementation
@@ -46,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;
@@ -83,6 +89,7 @@ private:
void toggle3DExtrusions(bool visible);
mbgl::Map* map = nullptr;
+ GLFWRendererFrontend* rendererFrontend = nullptr;
bool fullscreen = false;
const bool benchmark = false;
@@ -108,6 +115,7 @@ private:
std::function<void()> changeStyleCallback;
std::function<void()> pauseResumeCallback;
+ std::function<void()> onlineStatusCallback;
std::function<void(mbgl::Map*)> animateRouteCallback;
mbgl::util::RunLoop runLoop;
diff --git a/platform/glfw/main.cpp b/platform/glfw/main.cpp
index 7192475835..c4e0ecf692 100644
--- a/platform/glfw/main.cpp
+++ b/platform/glfw/main.cpp
@@ -1,4 +1,5 @@
#include "glfw_view.hpp"
+#include "glfw_renderer_frontend.hpp"
#include "settings_json.hpp"
#include <mbgl/util/default_styles.hpp>
@@ -7,11 +8,13 @@
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
#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>
@@ -32,69 +35,48 @@ void quit_handler(int) {
}
int main(int argc, char *argv[]) {
- bool fullscreen = false;
- bool benchmark = false;
- std::string style;
- double latitude = 0, longitude = 0;
- double bearing = 0, zoom = 1, pitch = 0;
- bool skipConfig = false;
-
- const struct option long_options[] = {
- {"fullscreen", no_argument, nullptr, 'f'},
- {"benchmark", no_argument, nullptr, 'b'},
- {"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 0:
- if (long_options[option_index].flag != nullptr)
- break;
- case 'f':
- fullscreen = true;
- break;
- case 'b':
- benchmark = true;
- break;
- case 's':
- style = std::string("asset://") + std::string(optarg);
- break;
- case 'x':
- longitude = atof(optarg);
- skipConfig = true;
- break;
- case 'y':
- latitude = atof(optarg);
- skipConfig = true;
- break;
- case 'z':
- zoom = atof(optarg);
- skipConfig = true;
- break;
- case 'r':
- bearing = atof(optarg);
- skipConfig = true;
- break;
- case 'p':
- pitch = atof(optarg);
- skipConfig = true;
- break;
- default:
- break;
- }
-
+ 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;
+ 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;
sigIntHandler.sa_handler = quit_handler;
@@ -110,6 +92,10 @@ int main(int argc, char *argv[]) {
view = &backend;
mbgl::DefaultFileSource fileSource("/tmp/mbgl-cache.db", ".");
+ if (!settings.online) {
+ fileSource.setOnlineStatus(false);
+ mbgl::Log::Warning(mbgl::Event::Setup, "Application is offline. Press `O` to toggle online status.");
+ }
// Set access token if present
const char *token = getenv("MAPBOX_ACCESS_TOKEN");
@@ -120,26 +106,26 @@ int main(int argc, char *argv[]) {
}
mbgl::ThreadPool threadPool(4);
-
- mbgl::Map map(backend, view->getSize(), view->getPixelRatio(), fileSource, threadPool);
+ GLFWRendererFrontend rendererFrontend { std::make_unique<mbgl::Renderer>(backend, view->getPixelRatio(), fileSource, threadPool), backend };
+ mbgl::Map map(rendererFrontend, backend, view->getSize(), view->getPixelRatio(), fileSource, threadPool);
backend.setMap(&map);
- // Load settings
- mbgl::Settings_JSON settings;
-
- if (skipConfig) {
- map.setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom);
- map.setBearing(bearing);
- map.setPitch(pitch);
- mbgl::Log::Info(mbgl::Event::General, "Location: %f/%f (z%.2f, %.2f deg)", latitude, longitude, zoom, bearing);
- } else {
- map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom);
- map.setBearing(settings.bearing);
- map.setPitch(settings.pitch);
- map.setDebug(mbgl::MapDebugOptions(settings.debug));
+ if (!style.empty() && style.find("://") == std::string::npos) {
+ style = std::string("file://") + style;
}
+ map.setLatLngZoom(mbgl::LatLng(settings.latitude, settings.longitude), settings.zoom);
+ map.setBearing(settings.bearing);
+ map.setPitch(settings.pitch);
+ map.setDebug(mbgl::MapDebugOptions(settings.debug));
+
+ view->setOnlineStatusCallback([&settings, &fileSource]() {
+ settings.online = !settings.online;
+ fileSource.setOnlineStatus(settings.online);
+ mbgl::Log::Info(mbgl::Event::Setup, "Application is %s. Press `O` to toggle online status.", settings.online ? "online" : "offline");
+ });
+
view->setChangeStyleCallback([&map] () {
static uint8_t currentStyleIndex;
@@ -191,13 +177,11 @@ int main(int argc, char *argv[]) {
settings.bearing = map.getBearing();
settings.pitch = map.getPitch();
settings.debug = mbgl::EnumType(map.getDebug());
- if (!skipConfig) {
- settings.save();
- }
+ settings.save();
mbgl::Log::Info(mbgl::Event::General,
R"(Exit location: --lat="%f" --lon="%f" --zoom="%f" --bearing "%f")",
settings.latitude, settings.longitude, settings.zoom, settings.bearing);
view = nullptr;
return 0;
-} \ No newline at end of file
+}
diff --git a/platform/glfw/settings_json.cpp b/platform/glfw/settings_json.cpp
index 2ba1038dc7..5b6aa4e0da 100644
--- a/platform/glfw/settings_json.cpp
+++ b/platform/glfw/settings_json.cpp
@@ -14,6 +14,7 @@ void Settings_JSON::load() {
file >> bearing;
file >> pitch;
file >> debug;
+ file >> online;
}
}
@@ -26,6 +27,7 @@ void Settings_JSON::save() {
file << bearing << std::endl;
file << pitch << std::endl;
file << debug << std::endl;
+ file << online << std::endl;
}
}
@@ -36,6 +38,7 @@ void Settings_JSON::clear() {
bearing = 0;
pitch = 0;
debug = 0;
+ online = true;
}
} // namespace mbgl
diff --git a/platform/glfw/settings_json.hpp b/platform/glfw/settings_json.hpp
index eb23b28bc8..c89accb8af 100644
--- a/platform/glfw/settings_json.hpp
+++ b/platform/glfw/settings_json.hpp
@@ -17,8 +17,12 @@ 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;
};
} // namespace mbgl
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index ce56d157c1..fba48e6e84 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -1,23 +1,104 @@
-# Changelog for Mapbox iOS SDK
+# Changelog for Mapbox Maps SDK for iOS
Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.
## master
+### Packaging
+
+* Renamed this SDK from Mapbox iOS SDK to Mapbox Maps SDK for iOS. ([#10610](https://github.com/mapbox/mapbox-gl-native/pull/10610))
+
+### Annotations and user interaction
+
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+
### Styles
-* Added suuport for diplaying georeferenced images via the `MGLImageSource`. [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
+* Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270))
+* Added `MGLComputedShapeSource` source class that allows applications to supply vector data on a per-tile basis.
+
+## 3.7.0
+
+### Networking and storage
+
+* Added a new `MGLMapSnapshotter` class for capturing rendered map images from an `MGLMapView`’s camera. ([#9891](https://github.com/mapbox/mapbox-gl-native/pull/9891))
+* Reduced the time it takes to create new `MGLMapView` instances in some cases. ([#9864](https://github.com/mapbox/mapbox-gl-native/pull/9864))
+* Added support for forced cache revalidation that will eliminate flickering that was sometimes visible for certain types of tiles (e.g., traffic tiles). ([#9670](https://github.com/mapbox/mapbox-gl-native/pull/9670), [#9103](https://github.com/mapbox/mapbox-gl-native/issues/9103))
+* Improved the performance of the SDK when parsing vector tile data used to render the map. ([#9312](https://github.com/mapbox/mapbox-gl-native/pull/9312))
+
+### Styles
+
+* Added a new type of source, represented by the `MGLImageSource` class at runtime, that displays a georeferenced image. ([#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110))
+* Setting a style using `MGLMapView`'s `styleURL` property now smoothly transitions from the previous style to the new style and maintains equivalent layers and sources along with their identifiers. ([#9256](https://github.com/mapbox/mapbox-gl-native/pull/9256))
+* Added `MGLCircleStyleLayer.circlePitchAlignment` and `MGLSymbolStyleLayer.iconPitchAlignment` properties to control whether circles and symbols lie flat against a tilted map. ([#9426](https://github.com/mapbox/mapbox-gl-native/pull/9426), [#9479](https://github.com/mapbox/mapbox-gl-native/pull/9479))
+* Added an `MGLSymbolStyleLayer.iconAnchor` property to control where an icon is anchored. ([#9849](https://github.com/mapbox/mapbox-gl-native/pull/9849))
+* The `maximumTextWidth` and `textLetterSpacing` properties of `MGLSymbolStyleLayer` are now compatible with `MGLSourceStyleFunction`s and `MGLCompositeStyleFunction`s, allowing data-driven styling of these properties. ([#9870](https://github.com/mapbox/mapbox-gl-native/pull/9870))
+* Improved the legibility of labels that follow lines when the map is tilted. ([#9009](https://github.com/mapbox/mapbox-gl-native/pull/9009))
+* Fixed an issue that could cause flickering when a translucent raster style layer was present. ([#9468](https://github.com/mapbox/mapbox-gl-native/pull/9468))
+* Fixed an issue that could cause antialiasing between polygons on the same layer to fail if the fill layers used data-driven styling for the fill color. ([#9699](https://github.com/mapbox/mapbox-gl-native/pull/9699))
+* The previously deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+
+### Annotations
+
+* Fixed several bugs and performance issues related to the use of annotations backed by `MGLAnnotationImage`. The limits on the number and size of images and glyphs has been effectively eliminated and should now depend on hardware constraints. These fixes also apply to images used to represent icons in `MGLSymbolStyleLayer`. ([#9213](https://github.com/mapbox/mapbox-gl-native/pull/9213))
+* Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617))
+* Selecting an annotation no longer sets the user tracking mode to `MGLUserTrackingModeNone`. ([#10094](https://github.com/mapbox/mapbox-gl-native/pull/10094))
+* Added `-[MGLMapView cameraThatFitsShape:direction:edgePadding:]` 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))
+* Added support selection of shape and polyline annotations.([#9984](https://github.com/mapbox/mapbox-gl-native/pull/9984))
+* Fixed an issue where view annotations could be slightly misaligned. View annotation placement is now rounded to the nearest pixel. ([#10219](https://github.com/mapbox/mapbox-gl-native/pull/10219))
+* Fixed an issue where a shape annotation callout was not displayed if the centroid was not visible. ([#10255](https://github.com/mapbox/mapbox-gl-native/pull/10255))
+
+### User interaction
+
+* Users of VoiceOver can now swipe left and right to navigate among visible places, points of interest, and roads. ([#9950](https://github.com/mapbox/mapbox-gl-native/pull/9950))
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+
+### Other changes
+
+* Added a Bulgarian localization. ([#10309](https://github.com/mapbox/mapbox-gl-native/pull/10309))
+* Fixed an issue that could cause line label rendering glitches when the line geometry is projected to a point behind the plane of the camera. ([#9865](https://github.com/mapbox/mapbox-gl-native/pull/9865))
+* Fixed an issue that could cause a crash when using `-[MGLMapView flyToCamera:completionHandler:]` and related methods with zoom levels at or near the maximum value. ([#9381](https://github.com/mapbox/mapbox-gl-native/pull/9381))
+* Added `-[MGLMapView showAttribution:]` to allow custom attribution buttons to show the default attribution interface. ([#10085](https://github.com/mapbox/mapbox-gl-native/pull/10085))
+* Fixed a conflict between multiple copies of SMCalloutView in a project. ([#10183](https://github.com/mapbox/mapbox-gl-native/pull/10183))
+* Fixed a crash when enabling the scale bar in iOS 8. ([#10241](https://github.com/mapbox/mapbox-gl-native/pull/10241))
+
+## 3.6.4 - September 25, 2017
+
+* Fixed an issue where stale (but still valid) map data could be ignored in offline mode. ([#10012](https://github.com/mapbox/mapbox-gl-native/pull/10012))
+
+## 3.6.3 - September 15, 2017
+
+* Added the option to display an always-on heading indicator with the default user location annotation, controlled via the `MGLMapView.showsUserHeadingIndicator` property. ([#9886](https://github.com/mapbox/mapbox-gl-native/pull/9886))
+* Fixed an issue where user heading tracking mode would update too frequently. ([#9845](https://github.com/mapbox/mapbox-gl-native/pull/9845))
+* Added support for iOS 11 location usage descriptions. ([#9869](https://github.com/mapbox/mapbox-gl-native/pull/9869))
+* Fixed an issue where `MGLUserLocation.location` did not follow its documented initialization behavior. This property will now properly return `nil` until the user’s location has been determined. ([#9639](https://github.com/mapbox/mapbox-gl-native/pull/9639))
+* `MGLMapView`’s `minimumZoomLevel` and `maximumZoomLevel` properties are now available in Interface Builder’s Attributes inspector. ([#9729](https://github.com/mapbox/mapbox-gl-native/pull/9729))
+* Deprecated `+[MGLStyle trafficDayStyleURL]` and `+[MGLStyle trafficNightStyleURL]` with no replacement method. To use the Traffic Day and Traffic Night styles going forward, we recommend that you use the underlying URL. ([#9918](https://github.com/mapbox/mapbox-gl-native/pull/9918))
+* Fixed a crash that sometimes occurred when a map view's view controller was deallocated. ([#9995](https://github.com/mapbox/mapbox-gl-native/pull/9995))
+
+## 3.6.2 - August 18, 2017
+
+* Added an `MGLStyle.localizesLabels` property, off by default, that localizes any Mapbox Streets–sourced symbol layer into the user’s preferred language. ([#9582](https://github.com/mapbox/mapbox-gl-native/pull/9582))
+* Added an additional camera method to MGLMapView that accepts an edge padding parameter. ([#9651](https://github.com/mapbox/mapbox-gl-native/pull/9651))
+* Fixed an issue with the scaling of the user location annotation’s horizontal accuracy indicator. ([#9721](https://github.com/mapbox/mapbox-gl-native/pull/9721))
+* Fixed an issue that caused `-[MGLShapeSource featuresMatchingPredicate:]` and `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` to always return an empty array. ([#9784](https://github.com/mapbox/mapbox-gl-native/pull/9784))
+
+## 3.6.1 - July 28, 2017
+
+* Reduced the size of the dynamic framework by optimizing symbol visibility. ([#7604](https://github.com/mapbox/mapbox-gl-native/pull/7604))
+* Fixed an issue where the attribution button would have its custom tint color reset when the map view received a tint color change notification, such as when an alert controller was presented. ([#9598](https://github.com/mapbox/mapbox-gl-native/pull/9598))
+* Improved the behavior of zoom gestures when the map reaches the minimum zoom limit. ([#9626](https://github.com/mapbox/mapbox-gl-native/pull/9626))
+* Fixed an issue where tilt gesture was triggered with two fingers aligned vertically and panning down. ([#9571](https://github.com/mapbox/mapbox-gl-native/pull/9571))
+* Bitcode symbol maps (.bcsymbolmap files) are now included with the dynamic framework. ([#9613](https://github.com/mapbox/mapbox-gl-native/pull/9613))
+
+## 3.6.0 - June 29, 2017
### Packaging
* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775))
* Fixed an issue in the static framework where localizations would never load. ([#9074](https://github.com/mapbox/mapbox-gl-native/pull/9074))
* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771), [#8773](https://github.com/mapbox/mapbox-gl-native/pull/8773))
-
-* The previously-deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
-* Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617))
-
-## 3.6.0
+* Added a Hungarian localization. ([#9347](https://github.com/mapbox/mapbox-gl-native/pull/9347))
### Styles
@@ -29,8 +110,11 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Fixed an issue preventing programmatically added style layers from appearing in already cached tiles. ([#8954](https://github.com/mapbox/mapbox-gl-native/pull/8954))
* Fixed an issue causing a composite function’s highest zoom level stop to be misinterpreted. ([#8613](https://github.com/mapbox/mapbox-gl-native/pull/8613), [#8790](https://github.com/mapbox/mapbox-gl-native/pull/8790))
* Fixed an issue where re-adding a layer that had been previously removed from a style would reset its paint properties. Moved initializers for `MGLTileSource`, `MGLStyleLayer`, and `MGLForegroundStyleLayer` to their concrete subclasses; because these classes were already intended for initialization only via concrete subclasses, this should have no developer impact. ([#8626](https://github.com/mapbox/mapbox-gl-native/pull/8626))
+* Fixed a crash that occurred when removing a source that was still being used by one or more style layers. Since this is a programming error, a warning is logged to the console instead. ([#9129](https://github.com/mapbox/mapbox-gl-native/pull/9129))
* Feature querying results now account for any changes to a feature’s size caused by a source or composite style function. ([#8665](https://github.com/mapbox/mapbox-gl-native/pull/8665))
+* Fixed the behavior of composite functions that specify fractional zoom level stops. ([#9289](https://github.com/mapbox/mapbox-gl-native/pull/9289))
* Letter spacing is now disabled in Arabic text so that ligatures are drawn correctly. ([#9062](https://github.com/mapbox/mapbox-gl-native/pull/9062))
+* Improved the performance of styles using source and composite style functions. ([#9185](https://github.com/mapbox/mapbox-gl-native/pull/9185), [#9257](https://github.com/mapbox/mapbox-gl-native/pull/9257))
### Annotations
@@ -44,10 +128,14 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
### User interaction
* Added a scale bar to `MGLMapView` that indicates the scale of the map. ([#7631](https://github.com/mapbox/mapbox-gl-native/pull/7631))
+* Fixed an issue causing the map to go blank during a flight animation that travels a very short distance. ([#9199](https://github.com/mapbox/mapbox-gl-native/pull/9199))
* Fixed an issue where gesture recognizers associated with map view interactivity were not disabled when their related interactions were disabled. ([#8304](https://github.com/mapbox/mapbox-gl-native/pull/8304))
* Fixed an issue preventing the Mapbox Telemetry confirmation dialog from appearing when opened from within a map view in a modal view controller. ([#9027](https://github.com/mapbox/mapbox-gl-native/pull/9027))
* Corrected the size of MGLMapView’s compass. ([#9060](https://github.com/mapbox/mapbox-gl-native/pull/9060))
* The Improve This Map button in the attribution action sheet now leads to a feedback tool that matches MGLMapView’s rotation and pitch. `-[MGLAttributionInfo feedbackURLAtCenterCoordinate:zoomLevel:]` no longer respects the feedback URL specified in TileJSON. ([#9078](https://github.com/mapbox/mapbox-gl-native/pull/9078))
+* `-[MGLMapViewDelegate mapView:shouldChangeFromCamera:toCamera:]` can now block any panning caused by a pinch gesture. ([#9344](https://github.com/mapbox/mapbox-gl-native/pull/9344))
+* If the user taps on the map while it is flying to the user’s location, the user dot no longer appears in the incorrect location. ([#7916](https://github.com/mapbox/mapbox-gl-native/pull/7916))
+* Improved the responsiveness of the tilt gesture by reducing the initial recognition delay. ([#9386](https://github.com/mapbox/mapbox-gl-native/pull/9386))
### Other changes
@@ -57,6 +145,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418))
* Improved CPU and battery performance while animating a tilted map’s camera in an area with many labels. ([#9031](https://github.com/mapbox/mapbox-gl-native/pull/9031))
* Fixed an issue rendering polylines that contain duplicate vertices. ([#8808](https://github.com/mapbox/mapbox-gl-native/pull/8808))
+* Added struct boxing to `MGLCoordinateSpan`, `MGLCoordinateBounds`, `MGLOfflinePackProgress`, and `MGLTransition`. ([#9343](https://github.com/mapbox/mapbox-gl-native/pull/9343))
## 3.5.4 - May 9, 2017
diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md
index 83064fbbd8..10a94c73f3 100644
--- a/platform/ios/DEVELOPING.md
+++ b/platform/ios/DEVELOPING.md
@@ -1,12 +1,12 @@
-# Contributing to the Mapbox iOS SDK
+# Contributing to the Mapbox Maps SDK for iOS
-This document explains how to build the Mapbox iOS SDK from source. It is intended for advanced developers who wish to contribute to Mapbox GL and the Mapbox iOS SDK.
+This document explains how to build the Mapbox Maps SDK for iOS from source. It is intended for advanced developers who wish to contribute to Mapbox GL and the Mapbox Maps SDK for iOS.
## Requirements
-The Mapbox iOS SDK and iosapp demo application require iOS 8.0 or above.
+The Mapbox Maps SDK for iOS and iosapp demo application require iOS 8.0 or above.
-The Mapbox iOS SDK requires Xcode 8.0 or above.
+The Mapbox Maps SDK for iOS requires Xcode 9.1 or above to compile from source.
## Building the SDK
@@ -30,7 +30,7 @@ Before building, use the scheme picker button in the toolbar to change the schem
* **static** builds the SDK as a static library and separate resource bundle.
* **dynamic+static** is a combination of the **dynamic** and **static** schemes.
-If you don’t have an Apple Developer account, change the destination to a simulator such as “iPhone 6s” before you run and build the app.
+If you don’t have an Apple Developer account, change the destination to a simulator such as “iPhone 6s” before you build and run the app.
### Packaging builds
@@ -46,19 +46,18 @@ Build and package the SDK by using one of the following commands:
* `make iframework` builds a dynamic framework in the Debug configuration for devices and the iOS Simulator. The CocoaPods pod downloads the output of this target.
* `make ipackage-sim` builds a dynamic framework in the Debug configuration for the iOS simulator. This is the fastest target.
* `make ipackage-strip` builds both dynamic and static frameworks in the Debug configuration, stripped of debug symbols, for devices and the iOS Simulator.
-* `make ifabric` builds a special static framework for compatibility with the Fabric Mac application.
You can customize the build output by passing the following arguments into the `make` invocation:
* `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.
@@ -71,44 +70,44 @@ To add any Objective-C type, constant, or member to the iOS SDK’s public inter
1. Ensure that the symbol is pure Objective-C and does not rely on any language features specific to Objective-C++ or the C11 dialect of C – so no namespaced types or classes named with emoji! 🙃 Most projects that depend on this SDK are either written in pure Objective-C (GNU99 dialect) or Swift, which cannot yet bridge C++ types.
1. Name the symbol according to [Cocoa naming conventions](https://developer.apple.com/library/prerelease/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html#//apple_ref/doc/uid/10000146i). Use the `MGL` class prefix to avoid conflicts with client code. If the symbol has an analogue in MapKit, name the symbol according to MapKit.
-1. Provide full documentation comments. We use [jazzy](https://github.com/realm/jazzy/) to produce the documentation found in the SDK distribution and [on the Mapbox iOS SDK website](https://www.mapbox.com/ios-sdk/api/). We also recognize that many developers rely on Xcode’s Quick Help feature. jazzy supports Markdown formatting; however, Quick Help supports only [HeaderDoc](https://developer.apple.com/legacy/library/documentation/DeveloperTools/Conceptual/HeaderDoc/intro/intro.html) syntax and a subset of Doxygen syntax. For hyperlinks, use HTML syntax, which is recognized by both tools.
+1. Provide full documentation comments. We use [jazzy](https://github.com/realm/jazzy/) to produce the documentation found in the SDK distribution and [on the website for this SDK](https://www.mapbox.com/ios-sdk/api/). We also recognize that many developers rely on Xcode’s Quick Help feature. jazzy supports Markdown formatting; however, Quick Help supports only [HeaderDoc](https://developer.apple.com/legacy/library/documentation/DeveloperTools/Conceptual/HeaderDoc/intro/intro.html) syntax and a subset of Doxygen syntax. For hyperlinks, use HTML syntax, which is recognized by both tools.
### Making a type or constant public
-To add an Objective-C class, protocol, category, typedef, enumeration, or global constant to the iOS SDK’s public interface:
+To add an Objective-C class, protocol, category, typedef, enumeration, or global constant to the iOS maps SDK’s public interface:
1. _(Optional.)_ Add the macro `MGL_EXPORT` prior to the declaration for classes and global constants when adding them in shared headers located in `platform/darwin`. To use this macro, include `MGLFoundation.h`. You can check whether all public symbols are exported correctly by running `make check-public-symbols`.
1. _(Optional.)_ Add the type or constant’s name to the relevant category in the `custom_categories` section of [the jazzy configuration file](./jazzy.yml). This is required for classes and protocols and also recommended for any other type that is strongly associated with a particular class or protocol. If you leave out this step, the symbol will appear in an “Other” section in the generated HTML documentation’s table of contents.
-1. _(Optional.)_ If the symbol would also be publicly exposed in the macOS SDK, consult [the companion macOS document](../macos/DEVELOPING.md#making-a-type-or-constant-public) for further instructions.
+1. _(Optional.)_ If the symbol would also be publicly exposed in the macOS maps SDK, consult [the companion macOS document](../macos/DEVELOPING.md#making-a-type-or-constant-public) for further instructions.
### Adding a source code file
-To add an Objective-C header or implementation file to the iOS SDK:
+To add an Objective-C header or implementation file to the iOS maps SDK:
1. Add the file to the Headers or Compile Sources build phase, as appropriate, of both the “dynamic” and “static” targets. You can either use the Build Phases tab of the project editor or the Target Membership section of the File inspector.
1. Audit new headers for nullability. Typically, you will wrap a header with `NS_ASSUME_NONNULL_BEGIN` and `NS_ASSUME_NONNULL_END`.
1. _(Optional.)_ If it’s a public header, change its visibility from Project to Public and import it in [the iOS SDK’s umbrella header](./src/Mapbox.h).
-1. _(Optional.)_ If the file would also be used by the macOS SDK, make sure it’s in [platform/darwin/src/](../darwin/src/), then consult [the companion macOS document](../macos/DEVELOPING.md#adding-a-source-code-file) for further instructions.
+1. _(Optional.)_ If the file would also be used by the macOS maps SDK, make sure it’s in [platform/darwin/src/](../darwin/src/), then consult [the companion macOS document](../macos/DEVELOPING.md#adding-a-source-code-file) for further instructions.
### Adding a resource
-To add a resource (such as an image, SSL certificate, property list, or strings table) to the iOS SDK:
+To add a resource (such as an image, SSL certificate, property list, or strings table) to the iOS maps SDK:
1. Add the header to the Copy Bundle Resources build phase of both the “dynamic” and “bundle” targets. You can either use the Build Phases tab of the project editor or the Target Membership section of the File inspector.
-1. _(Optional.)_ If the resource would also be used by the macOS SDK, make sure it’s in [platform/darwin/resources/](../darwin/resources/), then consult [the companion macOS document](../macos/DEVELOPING.md#adding-a-resource) for further instructions.
+1. _(Optional.)_ If the resource would also be used by the macOS maps SDK, make sure it’s in [platform/darwin/resources/](../darwin/resources/), then consult [the companion macOS document](../macos/DEVELOPING.md#adding-a-resource) for further instructions.
### Adding user-facing text
-To add or update text that the user may see in the iOS SDK:
+To add or update text that the user may see in the iOS maps SDK:
1. Make sure the implementation file imports [NSBundle+MGLAdditions.h](../darwin/src/NSBundle+MGLAdditions.h).
1. Use the `NSLocalizedStringWithDefaultValue()` macro:
* `key` is a unique identifier that won’t change if the user-facing text ever needs to change.
- * `tbl` is `Foundation` in code shared between the iOS and macOS SDKs, or `nil` otherwise.
+ * `tbl` is `Foundation` in code shared between the iOS and macOS maps SDKs, or `nil` otherwise.
* `bundle` is `nil`; the redefined macro looks for the SDK bundle at runtime and ignores this argument.
* `val` is the English string.
1. _(Optional.)_ When dealing with a number followed by a pluralized word, do not split the string. Instead, use a format string and make `val` ambiguous, like `%d file(s)`. Then pluralize for English in the appropriate [.stringsdict file](https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html). See [platform/darwin/resources/en.lproj/Foundation.stringsdict](../darwin/resources/en.lproj/Foundation.stringsdict) for an example. Localizers should do likewise for their languages.
-1. Run `make genstrings` and commit any changes it makes to .strings files. The make rule also updates the macOS SDK’s strings tables.
+1. Run `make genstrings` and commit any changes it makes to .strings files. The make rule also updates the macOS maps SDK’s strings tables.
### Adding a localization
@@ -152,13 +151,6 @@ make darwin-update-examples
`make ios-test` builds and runs unit tests of cross-platform code as well as the SDK.
-Before you can run unit tests of the cross-platform code on the command line, install ios-sim version 3.2.0 (not any other version):
-
-```bash
-brew tap mapbox/homebrew-ios-sim-3
-brew install mapbox/homebrew-ios-sim-3/ios-sim
-```
-
To instead run the cross-platform tests in Xcode instead of on the command line:
1. Run `make iproj` to set up the workspace.
@@ -176,5 +168,5 @@ The included applications use Mapbox vector tiles, which require a Mapbox accoun
- Use two fingers to rotate
- Double-tap to zoom in one level
- Two-finger single-tap to zoom out one level
-- Double-tap, long-pressing the second, then pan up and down to "quick zoom" (iPhone only, meant for one-handed use)
+- Double-tap, long-pressing the second, then pan up and down to "quick zoom" (meant for one-handed use)
- Use the debug menu to add test annotations, reset position, and cycle through the debug options.
diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md
index b64d9c650f..38fe33776c 100644
--- a/platform/ios/INSTALL.md
+++ b/platform/ios/INSTALL.md
@@ -1,16 +1,19 @@
-# Integrating custom builds of the Mapbox iOS SDK into your application
+# Integrating custom builds of the Mapbox Maps SDK for iOS into your application
-This document explains how to build a development version of Mapbox iOS SDK for use in your own Cocoa Touch application. To use a production-ready version of the SDK, see the [Mapbox iOS SDK homepage](https://mapbox.com/ios-sdk).
+This document explains how to build a development version of Mapbox Maps SDK for iOS for use in your own Cocoa Touch application. To use a production-ready version of the SDK, see the [Mapbox Maps SDK for iOS installation page](https://www.mapbox.com/install/ios/).
### Requirements
-The Mapbox iOS SDK is intended to run on iOS 8.0 and above on the following devices and their simulators:
+The Mapbox Maps SDK for iOS is intended to run on iOS 8.0 and above on the following devices and their simulators:
* iPhone 4S and above (5, 5c, 5s, 6, 6 Plus)
* iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2)
* iPod touch 5th generation and above
-The Mapbox iOS SDK requires Xcode 8.0 or higher. To use this SDK with Xcode 7.3.1, download and use a symbols build from the [releases](https://github.com/mapbox/mapbox-gl-native/releases) page.
+The Mapbox Maps SDK for iOS requires:
+
+* Xcode 9.1 or higher to compile from source
+* Xcode 8.0 or higher to integrate the compiled framework into an application
### Building the SDK
@@ -22,9 +25,8 @@ The Mapbox iOS SDK requires Xcode 8.0 or higher. To use this SDK with Xcode 7.3.
[sudo] gem install jazzy
```
-1. Run `make ipackage`. The packaging script will produce a `build/ios/pkg/` folder containing:
+1. Run `make iframework BUILDTYPE=Release`. The packaging script will produce a `build/ios/pkg/` folder containing:
- a `dynamic` folder containing a dynamically-linked fat framework with debug symbols for devices and the iOS Simulator
- - a `static` folder containing a statically-linked framework with debug symbols for devices and the iOS Simulator
- a `documentation` folder with HTML API documentation
- an example `Settings.bundle` containing an optional Mapbox Telemetry opt-out setting
@@ -32,7 +34,7 @@ See the [packaging documentation](DEVELOPING.md#packaging-builds) for other buil
### Installation
-There are several ways to install custom builds of the Mapbox iOS SDK:
+There are several ways to install custom builds of the Mapbox Maps SDK for iOS:
#### Dynamic framework
@@ -58,7 +60,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:
@@ -113,7 +115,7 @@ If using the static framework, add `$(inherited)` to your target’s Other Linke
#### Carthage
-For instructions on installing stable release versions of the Mapbox iOS SDK with Carthage, see [our website](https://www.mapbox.com/ios-sdk/). If you require a build without symbols pre-stripped, use [this feed URL](https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK-symbols.json) with Carthage.
+For instructions on installing stable release versions of the Mapbox Maps SDK for iOS with Carthage, see [our website](https://www.mapbox.com/install/ios/carthage/). If you require a build without symbols pre-stripped, use [this feed URL](https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK-symbols.json) with Carthage.
##### Testing pre-releases with Carthage
diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
index c7b14ead3b..b8b6687f0c 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.6.0-alpha.1'
+ version = '3.7.0-beta.4'
m.name = 'Mapbox-iOS-SDK-nightly-dynamic'
m.version = "#{version}-nightly"
diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
index 3116ede9f5..e84255715d 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.6.0-beta.2'
+ version = '3.7.0-beta.4'
m.name = 'Mapbox-iOS-SDK-symbols'
m.version = "#{version}-symbols"
diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec
index f6bc3030ab..bb6c5e0123 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.6.0-beta.2'
+ version = '3.7.0-beta.4'
m.name = 'Mapbox-iOS-SDK'
m.version = version
diff --git a/platform/ios/README.md b/platform/ios/README.md
index af01aed18d..23171e294e 100644
--- a/platform/ios/README.md
+++ b/platform/ios/README.md
@@ -1,12 +1,13 @@
-# [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)
+# [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/)
[![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.
-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.
+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, production-ready version instead.
-* [Integrating the Mapbox iOS SDK into your application](INSTALL.md)
-* [Contributing to the Mapbox iOS SDK](DEVELOPING.md)
+* [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 e1bc22272f..ccfb66f047 100644
--- a/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/platform/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -97,6 +97,12 @@
"idiom" : "ipad",
"filename" : "Icon-83.5@2x.png",
"scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-1024.png",
+ "scale" : "1x"
}
],
"info" : {
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/Assets.xcassets/settings.imageset/Contents.json b/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json
index 1eeddba9b9..228b81a818 100644
--- a/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json
+++ b/platform/ios/app/Assets.xcassets/settings.imageset/Contents.json
@@ -2,24 +2,11 @@
"images" : [
{
"idiom" : "universal",
- "filename" : "settings.png",
- "scale" : "1x"
- },
- {
- "idiom" : "universal",
- "filename" : "settings@2x.png",
- "scale" : "2x"
- },
- {
- "idiom" : "universal",
- "scale" : "3x"
+ "filename" : "settings.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
- },
- "properties" : {
- "template-rendering-intent" : "template"
}
} \ No newline at end of file
diff --git a/platform/ios/app/Assets.xcassets/settings.imageset/settings.pdf b/platform/ios/app/Assets.xcassets/settings.imageset/settings.pdf
new file mode 100644
index 0000000000..46aa7443f0
--- /dev/null
+++ b/platform/ios/app/Assets.xcassets/settings.imageset/settings.pdf
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/settings.imageset/settings.png b/platform/ios/app/Assets.xcassets/settings.imageset/settings.png
deleted file mode 100644
index 5d7643eef5..0000000000
--- a/platform/ios/app/Assets.xcassets/settings.imageset/settings.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png b/platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png
deleted file mode 100644
index 2bb9f0ebad..0000000000
--- a/platform/ios/app/Assets.xcassets/settings.imageset/settings@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/Default-568h@2x.png b/platform/ios/app/Default-568h@2x.png
deleted file mode 100644
index 0891b7aabf..0000000000
--- a/platform/ios/app/Default-568h@2x.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/app/Info.plist b/platform/ios/app/Info.plist
index d5b6825422..ea70ecd629 100644
--- a/platform/ios/app/Info.plist
+++ b/platform/ios/app/Info.plist
@@ -24,12 +24,16 @@
<string>7877</string>
<key>LSRequiresIPhoneOS</key>
<true/>
+ <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 ALWAYS display the user’s location.</string>
+ <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>
- <string>The map will display the user’s location.</string>
+ <string>The map will display your location.</string>
+ <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
+ <string>The map will display your location. If you choose Always, the map may also use your location when it isn’t visible in order to improve OpenStreetMap and Mapbox products.</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
@@ -51,5 +55,16 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
+ <key>UIApplicationShortcutItems</key>
+ <array>
+ <dict>
+ <key>UIApplicationShortcutItemTitle</key>
+ <string>Settings</string>
+ <key>UIApplicationShortcutItemType</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER).settings</string>
+ <key>UIApplicationShortcutItemIconFile</key>
+ <string>settings</string>
+ </dict>
+ </array>
</dict>
</plist>
diff --git a/platform/ios/app/LaunchScreen.storyboard b/platform/ios/app/LaunchScreen.storyboard
index 323bd43177..299e186886 100644
--- a/platform/ios/app/LaunchScreen.storyboard
+++ b/platform/ios/app/LaunchScreen.storyboard
@@ -1,8 +1,12 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <device id="retina4_7" orientation="portrait">
+ <adaptation id="fullscreen"/>
+ </device>
<dependencies>
<deployment identifier="iOS"/>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
@@ -14,9 +18,9 @@
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
- <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
diff --git a/platform/ios/app/MBXAppDelegate.m b/platform/ios/app/MBXAppDelegate.m
index c2834bfa7f..1934f4912b 100644
--- a/platform/ios/app/MBXAppDelegate.m
+++ b/platform/ios/app/MBXAppDelegate.m
@@ -26,4 +26,22 @@ NSString * const MBXMapboxAccessTokenDefaultsKey = @"MBXMapboxAccessToken";
return YES;
}
+#pragma mark - Quick actions
+
+- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
+ completionHandler([self handleShortcut:shortcutItem]);
+}
+
+- (BOOL)handleShortcut:(UIApplicationShortcutItem *)shortcut {
+ if ([[shortcut.type componentsSeparatedByString:@"."].lastObject isEqual:@"settings"]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
+ });
+
+ return YES;
+ }
+
+ return NO;
+}
+
@end
diff --git a/platform/ios/app/MBXSnapshotsViewController.h b/platform/ios/app/MBXSnapshotsViewController.h
new file mode 100644
index 0000000000..f791602e98
--- /dev/null
+++ b/platform/ios/app/MBXSnapshotsViewController.h
@@ -0,0 +1,5 @@
+#import <UIKit/UIKit.h>
+
+@interface MBXSnapshotsViewController : UIViewController
+
+@end
diff --git a/platform/ios/app/MBXSnapshotsViewController.m b/platform/ios/app/MBXSnapshotsViewController.m
new file mode 100644
index 0000000000..3bf93d8721
--- /dev/null
+++ b/platform/ios/app/MBXSnapshotsViewController.m
@@ -0,0 +1,66 @@
+#import "MBXSnapshotsViewController.h"
+
+#import <Mapbox/Mapbox.h>
+
+@interface MBXSnapshotsViewController ()
+
+// Top row
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewTL;
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewTM;
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewTR;
+
+// Bottom row
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewBL;
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewBM;
+@property (weak, nonatomic) IBOutlet UIImageView *snapshotImageViewBR;
+
+@end
+
+@implementation MBXSnapshotsViewController {
+ // Top row
+ MGLMapSnapshotter* topLeftSnapshotter;
+ MGLMapSnapshotter* topCenterSnapshotter;
+ MGLMapSnapshotter* topRightSnapshotter;
+
+ // Bottom row
+ MGLMapSnapshotter* bottomLeftSnapshotter;
+ MGLMapSnapshotter* bottomCenterSnapshotter;
+ MGLMapSnapshotter* bottomRightSnapshotter;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ // Start snapshotters
+ topLeftSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewTL coordinates:CLLocationCoordinate2DMake(37.7184, -122.4365)];
+ topCenterSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewTM coordinates:CLLocationCoordinate2DMake(38.8936, -77.0146)];
+ topRightSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewTR coordinates:CLLocationCoordinate2DMake(-13.1356, -74.2442)];
+
+ bottomLeftSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewBL coordinates:CLLocationCoordinate2DMake(52.5072, 13.4247)];
+ bottomCenterSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewBM coordinates:CLLocationCoordinate2DMake(60.2118, 24.6754)];
+ bottomRightSnapshotter = [self startSnapshotterForImageView:_snapshotImageViewBR coordinates:CLLocationCoordinate2DMake(31.2780, 121.4286)];
+}
+
+- (MGLMapSnapshotter*) startSnapshotterForImageView:(UIImageView*) imageView coordinates:(CLLocationCoordinate2D) coordinates {
+ // Create snapshot options
+ MGLMapCamera* mapCamera = [[MGLMapCamera alloc] init];
+ mapCamera.pitch = 20;
+ mapCamera.centerCoordinate = coordinates;
+ MGLMapSnapshotOptions* options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:[MGLStyle satelliteStreetsStyleURL] camera:mapCamera size:CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];
+ options.zoomLevel = 10;
+
+ // Create and start the snapshotter
+ MGLMapSnapshotter* snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options];
+ [snapshotter startWithCompletionHandler: ^(MGLMapSnapshot* snapshot, NSError *error) {
+ if (error) {
+ NSLog(@"Could not load snapshot: %@", [error localizedDescription]);
+ } else {
+ imageView.image = snapshot.image;
+ }
+ }];
+
+ return snapshotter;
+}
+
+
+@end
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 29c5c65012..4306354030 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -34,7 +34,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsCoreRenderingRows) {
MBXSettingsCoreRenderingTimestamps,
MBXSettingsCoreRenderingCollisionBoxes,
MBXSettingsCoreRenderingOverdrawVisualization,
- MBXSettingsCoreRenderingToggleTwoMaps,
};
typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) {
@@ -48,6 +47,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) {
MBXSettingsAnnotationsTestShapes,
MBXSettingsAnnotationsCustomCallout,
MBXSettingsAnnotationsQueryAnnotations,
+ MBXSettingsAnnotationsCustomUserDot,
MBXSettingsAnnotationsRemoveAnnotations,
};
@@ -73,17 +73,19 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
MBXSettingsRuntimeStylingVectorSource,
MBXSettingsRuntimeStylingRasterSource,
MBXSettingsRuntimeStylingImageSource,
- MBXSettingsRuntimeStylingCountryLabels,
MBXSettingsRuntimeStylingRouteLine,
MBXSettingsRuntimeStylingDDSPolygon,
+ MBXSettingsRuntimeStylingCustomLatLonGrid,
};
typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MBXSettingsMiscellaneousShowReuseQueueStats = 0,
MBXSettingsMiscellaneousWorldTour,
- MBXSettingsMiscellaneousCustomUserDot,
MBXSettingsMiscellaneousShowZoomLevel,
MBXSettingsMiscellaneousScrollView,
+ MBXSettingsMiscellaneousToggleTwoMaps,
+ MBXSettingsMiscellaneousCountryLabels,
+ MBXSettingsMiscellaneousShowSnapshots,
MBXSettingsMiscellaneousPrintLogFile,
MBXSettingsMiscellaneousDeleteLogFile,
};
@@ -110,11 +112,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@interface MBXViewController () <UITableViewDelegate,
UITableViewDataSource,
- MGLMapViewDelegate>
+ MGLMapViewDelegate,
+ MGLComputedShapeSourceDataSource>
@property (nonatomic) IBOutlet MGLMapView *mapView;
-@property (weak, nonatomic) IBOutlet UILabel *hudLabel;
+@property (weak, nonatomic) IBOutlet UIButton *hudLabel;
@property (nonatomic) NSInteger styleIndex;
@property (nonatomic) BOOL debugLoggingEnabled;
@property (nonatomic) BOOL customUserLocationAnnnotationEnabled;
@@ -162,7 +165,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"];
self.mapView.scaleBar.hidden = NO;
+ self.mapView.showsUserHeadingIndicator = YES;
self.hudLabel.hidden = YES;
+ if ([UIFont respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) {
+ self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular];
+ }
if ([MGLAccountManager accessToken].length)
{
@@ -305,8 +312,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
(debugMask & MGLMapDebugCollisionBoxesMask ? @"Hide" :@"Show")],
[NSString stringWithFormat:@"%@ Overdraw Visualization",
(debugMask & MGLMapDebugOverdrawVisualizationMask ? @"Hide" :@"Show")],
- [NSString stringWithFormat:@"%@ Second Map",
- ([self.view viewWithTag:2] == nil ? @"Show" : @"Hide")],
]];
break;
case MBXSettingsAnnotations:
@@ -321,6 +326,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Add Test Shapes",
@"Add Point With Custom Callout",
@"Query Annotations",
+ [NSString stringWithFormat:@"%@ Custom User Dot", (_customUserLocationAnnnotationEnabled ? @"Disable" : @"Enable")],
@"Remove Annotations",
]];
break;
@@ -347,18 +353,20 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Style Vector Source",
@"Style Raster Source",
@"Style Image Source",
- [NSString stringWithFormat:@"Label Countries in %@", (_usingLocaleBasedCountryLabels ? @"Local Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])],
@"Add Route Line",
@"Dynamically Style Polygon",
+ @"Add Custom Lat/Lon Grid",
]];
break;
case MBXSettingsMiscellaneous:
[settingsTitles addObjectsFromArray:@[
[NSString stringWithFormat:@"%@ Reuse Queue Stats", (_reuseQueueStatsEnabled ? @"Hide" :@"Show")],
@"Start World Tour",
- [NSString stringWithFormat:@"%@ Custom User Dot", (_customUserLocationAnnnotationEnabled ? @"Disable" : @"Enable")],
- [NSString stringWithFormat:@"%@ Zoom Level", (_showZoomLevelEnabled ? @"Hide" :@"Show")],
+ [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]])],
+ @"Show Snapshots"
]];
if (self.debugLoggingEnabled)
@@ -403,81 +411,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsCoreRenderingOverdrawVisualization:
self.mapView.debugMask ^= MGLMapDebugOverdrawVisualizationMask;
break;
- case MBXSettingsCoreRenderingToggleTwoMaps:
- if ([self.view viewWithTag:2] == nil) {
- MGLMapView *secondMapView = [[MGLMapView alloc] initWithFrame:
- CGRectMake(0, self.view.bounds.size.height / 2,
- self.view.bounds.size.width, self.view.bounds.size.height / 2)];
- secondMapView.translatesAutoresizingMaskIntoConstraints = false;
- secondMapView.tag = 2;
- for (NSLayoutConstraint *constraint in self.view.constraints)
- {
- if ((constraint.firstItem == self.mapView && constraint.firstAttribute == NSLayoutAttributeBottom) ||
- (constraint.secondItem == self.mapView && constraint.secondAttribute == NSLayoutAttributeBottom))
- {
- [self.view removeConstraint:constraint];
- break;
- }
- }
- [self.view addSubview:secondMapView];
- [self.view addConstraints:@[
- [NSLayoutConstraint constraintWithItem:self.mapView
- attribute:NSLayoutAttributeBottom
- relatedBy:NSLayoutRelationEqual
- toItem:self.view
- attribute:NSLayoutAttributeCenterY
- multiplier:1
- constant:0],
- [NSLayoutConstraint constraintWithItem:secondMapView
- attribute:NSLayoutAttributeCenterX
- relatedBy:NSLayoutRelationEqual
- toItem:self.view
- attribute:NSLayoutAttributeCenterX
- multiplier:1
- constant:0],
- [NSLayoutConstraint constraintWithItem:secondMapView
- attribute:NSLayoutAttributeWidth
- relatedBy:NSLayoutRelationEqual
- toItem:self.view
- attribute:NSLayoutAttributeWidth
- multiplier:1
- constant:0],
- [NSLayoutConstraint constraintWithItem:secondMapView
- attribute:NSLayoutAttributeTop
- relatedBy:NSLayoutRelationEqual
- toItem:self.view
- attribute:NSLayoutAttributeCenterY
- multiplier:1
- constant:0],
- [NSLayoutConstraint constraintWithItem:secondMapView
- attribute:NSLayoutAttributeBottom
- relatedBy:NSLayoutRelationEqual
- toItem:self.bottomLayoutGuide
- attribute:NSLayoutAttributeTop
- multiplier:1
- constant:0],
- ]];
- } else {
- NSMutableArray *constraintsToRemove = [NSMutableArray array];
- MGLMapView *secondMapView = (MGLMapView *)[self.view viewWithTag:2];
- for (NSLayoutConstraint *constraint in self.view.constraints)
- {
- if (constraint.firstItem == secondMapView || constraint.secondItem == secondMapView)
- {
- [constraintsToRemove addObject:constraint];
- }
- }
- [self.view removeConstraints:constraintsToRemove];
- [secondMapView removeFromSuperview];
- [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.mapView
- attribute:NSLayoutAttributeBottom
- relatedBy:NSLayoutRelationEqual
- toItem:self.bottomLayoutGuide
- attribute:NSLayoutAttributeTop
- multiplier:1
- constant:0]];
- }
- break;
default:
NSAssert(NO, @"All core rendering setting rows should be implemented");
break;
@@ -516,6 +449,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsAnnotationsQueryAnnotations:
[self testQueryPointAnnotations];
break;
+ case MBXSettingsAnnotationsCustomUserDot:
+ [self toggleCustomUserDot];
+ break;
case MBXSettingsAnnotationsRemoveAnnotations:
[self.mapView removeAnnotations:self.mapView.annotations];
break;
@@ -590,15 +526,15 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsRuntimeStylingImageSource:
[self styleImageSource];
break;
- case MBXSettingsRuntimeStylingCountryLabels:
- [self styleCountryLabelsLanguage];
- break;
case MBXSettingsRuntimeStylingRouteLine:
[self styleRouteLine];
break;
case MBXSettingsRuntimeStylingDDSPolygon:
[self stylePolygonWithDDS];
break;
+ case MBXSettingsRuntimeStylingCustomLatLonGrid:
+ [self addLatLonGrid];
+ break;
default:
NSAssert(NO, @"All runtime styling setting rows should be implemented");
break;
@@ -607,12 +543,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsMiscellaneous:
switch (indexPath.row)
{
+ case MBXSettingsMiscellaneousCountryLabels:
+ [self styleCountryLabelsLanguage];
+ break;
case MBXSettingsMiscellaneousWorldTour:
[self startWorldTour];
break;
- case MBXSettingsMiscellaneousCustomUserDot:
- [self toggleCustomUserDot];
- break;
case MBXSettingsMiscellaneousPrintLogFile:
[self printTelemetryLogFile];
break;
@@ -624,6 +560,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
self.reuseQueueStatsEnabled = !self.reuseQueueStatsEnabled;
self.hudLabel.hidden = !self.reuseQueueStatsEnabled;
self.showZoomLevelEnabled = NO;
+ [self updateHUD];
break;
}
case MBXSettingsMiscellaneousShowZoomLevel:
@@ -631,6 +568,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
self.showZoomLevelEnabled = !self.showZoomLevelEnabled;
self.hudLabel.hidden = !self.showZoomLevelEnabled;
self.reuseQueueStatsEnabled = NO;
+ [self updateHUD];
break;
}
case MBXSettingsMiscellaneousScrollView:
@@ -640,6 +578,86 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.navigationController pushViewController:embeddedMapViewController animated:YES];
break;
}
+ case MBXSettingsMiscellaneousToggleTwoMaps:
+ if ([self.view viewWithTag:2] == nil) {
+ MGLMapView *secondMapView = [[MGLMapView alloc] initWithFrame:
+ CGRectMake(0, self.view.bounds.size.height / 2,
+ self.view.bounds.size.width, self.view.bounds.size.height / 2)];
+ secondMapView.translatesAutoresizingMaskIntoConstraints = false;
+ secondMapView.tag = 2;
+ for (NSLayoutConstraint *constraint in self.view.constraints)
+ {
+ if ((constraint.firstItem == self.mapView && constraint.firstAttribute == NSLayoutAttributeBottom) ||
+ (constraint.secondItem == self.mapView && constraint.secondAttribute == NSLayoutAttributeBottom))
+ {
+ [self.view removeConstraint:constraint];
+ break;
+ }
+ }
+ [self.view addSubview:secondMapView];
+ [self.view addConstraints:@[
+ [NSLayoutConstraint constraintWithItem:self.mapView
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.view
+ attribute:NSLayoutAttributeCenterY
+ multiplier:1
+ constant:0],
+ [NSLayoutConstraint constraintWithItem:secondMapView
+ attribute:NSLayoutAttributeCenterX
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.view
+ attribute:NSLayoutAttributeCenterX
+ multiplier:1
+ constant:0],
+ [NSLayoutConstraint constraintWithItem:secondMapView
+ attribute:NSLayoutAttributeWidth
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.view
+ attribute:NSLayoutAttributeWidth
+ multiplier:1
+ constant:0],
+ [NSLayoutConstraint constraintWithItem:secondMapView
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.view
+ attribute:NSLayoutAttributeCenterY
+ multiplier:1
+ constant:0],
+ [NSLayoutConstraint constraintWithItem:secondMapView
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ multiplier:1
+ constant:0],
+ ]];
+ } else {
+ NSMutableArray *constraintsToRemove = [NSMutableArray array];
+ MGLMapView *secondMapView = (MGLMapView *)[self.view viewWithTag:2];
+ for (NSLayoutConstraint *constraint in self.view.constraints)
+ {
+ if (constraint.firstItem == secondMapView || constraint.secondItem == secondMapView)
+ {
+ [constraintsToRemove addObject:constraint];
+ }
+ }
+ [self.view removeConstraints:constraintsToRemove];
+ [secondMapView removeFromSuperview];
+ [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.mapView
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ multiplier:1
+ constant:0]];
+ }
+ break;
+ case MBXSettingsMiscellaneousShowSnapshots:
+ {
+ [self performSegueWithIdentifier:@"ShowSnapshots" sender:nil];
+ break;
+ }
default:
NSAssert(NO, @"All miscellaneous setting rows should be implemented");
break;
@@ -817,6 +835,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
}
MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]];
+ polygon.title = feature[@"properties"][@"NAME"];
[self.mapView addAnnotation:polygon];
@@ -1303,8 +1322,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)styleRasterSource
{
- NSArray *tileURLTemplates = @[@"https://stamen-tiles.a.ssl.fastly.net/terrain-background/{z}/{x}/{y}.jpg"];
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"style-raster-source-id" tileURLTemplates:tileURLTemplates options:@{
+ 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:@{
MGLTileSourceOptionTileSize: @256,
}];
[self.mapView.style addSource:rasterSource];
@@ -1348,12 +1367,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
-(void)styleCountryLabelsLanguage
{
- NSArray<NSString *> *labelLayers = @[
- @"country-label-lg",
- @"country-label-md",
- @"country-label-sm",
- ];
- [self styleLabelLanguageForLayersNamed:labelLayers];
+ _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels;
+ self.mapView.style.localizesLabels = _usingLocaleBasedCountryLabels;
}
- (void)styleRouteLine
@@ -1436,6 +1451,21 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[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 = [MGLStyleValue valueWithRawValue:@"{value}"];
+ [self.mapView.style addLayer:labelLayer];
+}
+
- (void)styleLabelLanguageForLayersNamed:(NSArray<NSString *> *)layers
{
_usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels;
@@ -1446,8 +1476,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MGLSymbolStyleLayer *layer = (MGLSymbolStyleLayer *)[self.mapView.style layerWithIdentifier:layerName];
if ([layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
- if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
- MGLConstantStyleValue *label = (MGLConstantStyleValue<NSString *> *)layer.text;
+ if ([layer.text isKindOfClass:[MGLStyleConstantValue class]]) {
+ MGLStyleConstantValue *label = (MGLStyleConstantValue<NSString *> *)layer.text;
if ([label.rawValue hasPrefix:@"{name"]) {
layer.text = [MGLStyleValue valueWithRawValue:language];
}
@@ -1455,7 +1485,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
NSMutableDictionary *stops = function.stops.mutableCopy;
- [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) {
+ [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLStyleConstantValue<NSString *> *stop, BOOL *done) {
if ([stop.rawValue hasPrefix:@"{name"]) {
stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:language];
}
@@ -1628,8 +1658,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[MGLStyle darkStyleURL],
[MGLStyle satelliteStyleURL],
[MGLStyle satelliteStreetsStyleURL],
- [MGLStyle trafficDayStyleURL],
- [MGLStyle trafficNightStyleURL],
+ [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"],
+ [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"],
+
];
NSAssert(styleNames.count == styleURLs.count, @"Style names and URLs don’t match.");
@@ -1686,7 +1717,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[sender setAccessibilityValue:nextAccessibilityValue];
}
-#pragma mark - Map Delegate
+#pragma mark - MGLMapViewDelegate
- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAnnotation>)annotation
{
@@ -1904,22 +1935,81 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)mapViewRegionIsChanging:(MGLMapView *)mapView
{
+ [self updateHUD];
+}
+
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
+ [self updateHUD];
+}
+
+- (void)mapView:(MGLMapView *)mapView didUpdateUserLocation:(MGLUserLocation *)userLocation {
+ [self updateHUD];
+}
+
+- (void)updateHUD {
+ if (!self.reuseQueueStatsEnabled && !self.showZoomLevelEnabled) return;
+
+ NSString *hudString;
+
if (self.reuseQueueStatsEnabled) {
NSUInteger queuedAnnotations = 0;
- for (NSArray *queue in self.mapView.annotationViewReuseQueueByIdentifier.allValues)
- {
+ for (NSArray *queue in self.mapView.annotationViewReuseQueueByIdentifier.allValues) {
queuedAnnotations += queue.count;
}
- self.hudLabel.text = [NSString stringWithFormat:@" Visible: %ld Queued: %ld", (unsigned long)mapView.visibleAnnotations.count, (unsigned long)queuedAnnotations];
+ hudString = [NSString stringWithFormat:@"Visible: %ld Queued: %ld", (unsigned long)self.mapView.visibleAnnotations.count, (unsigned long)queuedAnnotations];
} else if (self.showZoomLevelEnabled) {
- self.hudLabel.text = [NSString stringWithFormat:@" Zoom: %.2f", self.mapView.zoomLevel];
+ hudString = [NSString stringWithFormat:@"%.2f ∕ ↕\U0000FE0E%.f° ∕ %.f°", self.mapView.zoomLevel, self.mapView.camera.pitch, self.mapView.direction];
}
-}
-- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
- if (self.showZoomLevelEnabled) {
- self.hudLabel.text = [NSString stringWithFormat:@" Zoom: %.2f", self.mapView.zoomLevel];
+ [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 40198146ab..507582213f 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="11762" systemVersion="16D32" 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="12121" systemVersion="16G8c" 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="11757"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<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"/>
@@ -33,17 +33,24 @@
<outletCollection property="gestureRecognizers" destination="lfd-mn-7en" appends="YES" id="0PH-gH-GRm"/>
</connections>
</view>
- <label opaque="NO" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="58y-pX-YyB">
- <rect key="frame" x="179" y="626" width="180" height="21"/>
+ <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" constant="180" id="OL2-l5-I2f"/>
- <constraint firstAttribute="height" constant="21" id="xHg-ye-wzT"/>
+ <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="8"/>
- <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
- <nil key="highlightedColor"/>
- </label>
+ <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>
@@ -51,7 +58,8 @@
<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="m8o-i7-QIy" firstAttribute="top" secondItem="58y-pX-YyB" secondAttribute="bottom" constant="20" id="cjh-ZS-Mv4"/>
+ <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>
@@ -66,7 +74,7 @@
</connections>
</barButtonItem>
<button key="titleView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="KsN-ny-Hou">
- <rect key="frame" x="61" y="7" width="207" height="30"/>
+ <rect key="frame" x="65" y="5.5" width="203" height="33"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
<state key="normal" title="Streets"/>
@@ -94,8 +102,9 @@
</rightBarButtonItems>
</navigationItem>
<connections>
- <outlet property="hudLabel" destination="58y-pX-YyB" id="MEh-ir-3IH"/>
+ <outlet property="hudLabel" destination="58y-pX-YyB" id="aGG-7a-bZR"/>
<outlet property="mapView" destination="kNe-zV-9ha" id="VNR-WO-1q4"/>
+ <segue destination="zvf-Qd-4Ru" kind="show" identifier="ShowSnapshots" id="hzX-Jp-UJq"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="AAd-8J-9UU" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -352,6 +361,81 @@
</objects>
<point key="canvasLocation" x="594.39999999999998" y="1083.5082458770617"/>
</scene>
+ <!--Snapshots View Controller-->
+ <scene sceneID="Ooh-2U-4Bz">
+ <objects>
+ <viewController id="zvf-Qd-4Ru" customClass="MBXSnapshotsViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="ZLI-ej-4Bs"/>
+ <viewControllerLayoutGuide type="bottom" id="fiS-dq-r4S"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Jxm-v6-zI0">
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="miZ-Fw-EWq" userLabel="Image View TL">
+ <rect key="frame" x="0.0" y="64" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="XuN-T4-Z83" userLabel="Image View TM">
+ <rect key="frame" x="125" y="64" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ykR-Ku-i9l" userLabel="Image View TR">
+ <rect key="frame" x="250" y="64" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TL0-V8-T2F" userLabel="Image View BL">
+ <rect key="frame" x="0.0" y="365.5" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="eMy-JU-rq4" userLabel="Image View BM">
+ <rect key="frame" x="125" y="365.5" width="125" height="301.5"/>
+ </imageView>
+ <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="zT0-3J-0xw" userLabel="Image View BR">
+ <rect key="frame" x="250" y="365.5" width="125" height="301.5"/>
+ </imageView>
+ </subviews>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ <constraints>
+ <constraint firstItem="eMy-JU-rq4" firstAttribute="leading" secondItem="TL0-V8-T2F" secondAttribute="trailing" id="0xP-ii-cyV"/>
+ <constraint firstItem="eMy-JU-rq4" firstAttribute="top" secondItem="XuN-T4-Z83" secondAttribute="bottom" id="1HV-Tp-mUB"/>
+ <constraint firstItem="TL0-V8-T2F" firstAttribute="leading" secondItem="Jxm-v6-zI0" secondAttribute="leading" id="3fH-bn-5ND"/>
+ <constraint firstItem="miZ-Fw-EWq" firstAttribute="leading" secondItem="Jxm-v6-zI0" secondAttribute="leading" id="4yV-CW-c5n"/>
+ <constraint firstItem="fiS-dq-r4S" firstAttribute="top" secondItem="eMy-JU-rq4" secondAttribute="bottom" id="57P-Qo-M11"/>
+ <constraint firstItem="ykR-Ku-i9l" firstAttribute="top" secondItem="ZLI-ej-4Bs" secondAttribute="bottom" id="ARo-Nk-uVV"/>
+ <constraint firstAttribute="trailing" secondItem="ykR-Ku-i9l" secondAttribute="trailing" id="BRi-93-PGb"/>
+ <constraint firstItem="eMy-JU-rq4" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="FqJ-zb-pkb"/>
+ <constraint firstItem="TL0-V8-T2F" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="GrM-9L-dba"/>
+ <constraint firstItem="XuN-T4-Z83" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="HSd-2T-Kz7"/>
+ <constraint firstAttribute="trailing" secondItem="zT0-3J-0xw" secondAttribute="trailing" id="HaC-la-079"/>
+ <constraint firstItem="fiS-dq-r4S" firstAttribute="top" secondItem="TL0-V8-T2F" secondAttribute="bottom" id="JgE-s8-RAh"/>
+ <constraint firstItem="zT0-3J-0xw" firstAttribute="top" secondItem="ykR-Ku-i9l" secondAttribute="bottom" id="KQm-ue-i3z"/>
+ <constraint firstItem="zT0-3J-0xw" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="LUI-BF-66V"/>
+ <constraint firstItem="fiS-dq-r4S" firstAttribute="top" secondItem="zT0-3J-0xw" secondAttribute="bottom" id="MAe-3N-78O"/>
+ <constraint firstItem="TL0-V8-T2F" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="OvH-2m-yli"/>
+ <constraint firstItem="XuN-T4-Z83" firstAttribute="top" secondItem="ZLI-ej-4Bs" secondAttribute="bottom" id="bzY-6Y-K80"/>
+ <constraint firstItem="XuN-T4-Z83" firstAttribute="leading" secondItem="miZ-Fw-EWq" secondAttribute="trailing" id="jhf-gz-4UF"/>
+ <constraint firstItem="eMy-JU-rq4" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="l3m-tf-b1h"/>
+ <constraint firstItem="ykR-Ku-i9l" firstAttribute="leading" secondItem="XuN-T4-Z83" secondAttribute="trailing" id="oEV-Yi-iLs"/>
+ <constraint firstItem="TL0-V8-T2F" firstAttribute="top" secondItem="miZ-Fw-EWq" secondAttribute="bottom" id="oLW-zh-Fnk"/>
+ <constraint firstItem="miZ-Fw-EWq" firstAttribute="top" secondItem="ZLI-ej-4Bs" secondAttribute="bottom" id="qpD-mN-wfP"/>
+ <constraint firstItem="ykR-Ku-i9l" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="sP4-HJ-Vgk"/>
+ <constraint firstItem="XuN-T4-Z83" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="sTw-zD-Jid"/>
+ <constraint firstItem="zT0-3J-0xw" firstAttribute="height" secondItem="miZ-Fw-EWq" secondAttribute="height" id="t0u-eQ-Ail"/>
+ <constraint firstItem="ykR-Ku-i9l" firstAttribute="width" secondItem="miZ-Fw-EWq" secondAttribute="width" id="uQU-pB-kvq"/>
+ <constraint firstItem="zT0-3J-0xw" firstAttribute="leading" secondItem="eMy-JU-rq4" secondAttribute="trailing" id="w8M-MN-cmx"/>
+ </constraints>
+ </view>
+ <connections>
+ <outlet property="snapshotImageViewBL" destination="TL0-V8-T2F" id="e6C-dB-kHm"/>
+ <outlet property="snapshotImageViewBM" destination="eMy-JU-rq4" id="zeR-3U-EbH"/>
+ <outlet property="snapshotImageViewBR" destination="zT0-3J-0xw" id="6YR-lR-ela"/>
+ <outlet property="snapshotImageViewTL" destination="miZ-Fw-EWq" id="2Jj-kh-3Zw"/>
+ <outlet property="snapshotImageViewTM" destination="XuN-T4-Z83" id="MXY-7F-jB2"/>
+ <outlet property="snapshotImageViewTR" destination="ykR-Ku-i9l" id="aEL-Sg-RIW"/>
+ </connections>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="5xV-Ua-pqK" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="1365.5999999999999" y="1083.5082458770617"/>
+ </scene>
</scenes>
<resources>
<image name="TrackingLocationOffMask.png" width="23" height="23"/>
diff --git a/platform/ios/app/bg.lproj/Localizable.strings b/platform/ios/app/bg.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/ios/app/bg.lproj/Localizable.strings
diff --git a/platform/ios/app/hu.lproj/Localizable.strings b/platform/ios/app/hu.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/ios/app/hu.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/bitrise.yml b/platform/ios/bitrise.yml
index 53287ec06d..24bd054dbc 100644
--- a/platform/ios/bitrise.yml
+++ b/platform/ios/bitrise.yml
@@ -4,6 +4,8 @@ default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
trigger_map:
- pattern: nightly-release
workflow: nightly-release
+- pattern: release-from-tag
+ workflow: release-from-tag
- pattern: "*"
is_pull_request_allowed: true
workflow: primary
@@ -11,51 +13,54 @@ workflows:
primary:
steps:
- script:
+ title: Skip Workflow
+ inputs:
+ - content: echo "This workflow is obsolete — see CircleCi."
+ nightly-release:
+ steps:
+ - script:
title: Install Dependencies
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
brew install cmake
- brew tap mapbox/homebrew-ios-sim-3
- brew install mapbox/homebrew-ios-sim-3/ios-sim
- is_debug: 'yes'
- script:
- title: Generate Workspace
+ title: Configure AWS-CLI
+ inputs:
+ - content: |-
+ #!/bin/bash
+ pip install awscli
+ - script:
+ title: Build package
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
- export BUILDTYPE=Debug
- make iproj
+ export BUILDTYPE=Release
+ export BUILD_DEVICE=true
+ export FORMAT=dynamic
+ make ipackage-strip
+ CLOUDWATCH=true platform/ios/scripts/metrics.sh
+ platform/ios/scripts/deploy-nightly.sh
- is_debug: 'yes'
- - xcode-test:
- title: Run SDK Unit Tests
- inputs:
- - project_path: platform/ios/ios.xcworkspace
- - scheme: CI
- - deploy-to-bitrise-io:
- title: Deploy to Bitrise.io
- inputs:
- - notify_user_groups: none
- slack:
title: Post to Slack
inputs:
- webhook_url: "$SLACK_HOOK_URL"
- channel: "#gl-bots"
- - from_username: 'Bitrise iOS'
- - from_username_on_error: 'Bitrise iOS'
+ - from_username: 'Bitrise iOS Nightly 💤'
+ - from_username_on_error: 'Bitrise iOS Nightly 💤'
- message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- passed'
+ for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}@%7B1day%7D...${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
+ completed successfully.'
- message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- failed'
+ for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}@%7B1day%7D...${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
+ failed.'
- icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
- nightly-release:
+ release-from-tag:
steps:
- script:
title: Install Dependencies
@@ -64,39 +69,30 @@ workflows:
#!/bin/bash
set -eu -o pipefail
brew install cmake
+ sudo easy_install pip
+ sudo pip install awscli
- is_debug: 'yes'
- script:
- title: Configure AWS-CLI
- inputs:
- - content: |-
- #!/bin/bash
- apt-get install -y python-pip python-dev build-essential
- pip install awscli
- - script:
title: Build package
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
- export BUILDTYPE=Release
- export BUILD_DEVICE=true
- export FORMAT=dynamic
- make ipackage-strip
- CLOUDWATCH=true platform/ios/scripts/metrics.sh
- platform/ios/scripts/deploy-nightly.sh
+ export VERSION_TAG=${BITRISE_GIT_TAG}
+ platform/ios/scripts/deploy-packages.sh
- is_debug: 'yes'
- slack:
title: Post to Slack
inputs:
- webhook_url: "$SLACK_HOOK_URL"
- channel: "#gl-bots"
- - from_username: 'Bitrise iOS Nightly \U0001F31D'
- - from_username_on_error: 'Bitrise iOS Nightly \U0001F31D'
+ - from_username: 'Bitrise iOS Deploy'
+ - from_username_on_error: 'Bitrise iOS Deploy'
- message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}@%7B1day%7D...${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
+ for <https://github.com/mapbox/mapbox-gl-native/releases/tag/${BITRISE_GIT_TAG}|`${BITRISE_GIT_TAG}`>
completed successfully.'
- message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}@%7B1day%7D...${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
+ for <https://github.com/mapbox/mapbox-gl-native/releases/tag/${BITRISE_GIT_TAG}|`${BITRISE_GIT_TAG}`>
failed.'
- icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
diff --git a/platform/ios/config.cmake b/platform/ios/config.cmake
index fdb286a6d1..1caf372b25 100644
--- a/platform/ios/config.cmake
+++ b/platform/ios/config.cmake
@@ -2,45 +2,39 @@ add_definitions(-DMBGL_USE_GLES2=1)
mason_use(icu VERSION 58.1-min-size)
-macro(mbgl_platform_core)
- set_xcode_property(mbgl-core IPHONEOS_DEPLOYMENT_TARGET "8.0")
- set_xcode_property(mbgl-core ENABLE_BITCODE "YES")
- set_xcode_property(mbgl-core BITCODE_GENERATION_MODE bitcode)
- set_xcode_property(mbgl-core ONLY_ACTIVE_ARCH $<$<CONFIG:Debug>:YES>)
+macro(initialize_ios_target target)
+ set_xcode_property(${target} IPHONEOS_DEPLOYMENT_TARGET "8.0")
+ set_xcode_property(${target} ENABLE_BITCODE "YES")
+ set_xcode_property(${target} BITCODE_GENERATION_MODE bitcode)
+ set_xcode_property(${target} ONLY_ACTIVE_ARCH $<$<CONFIG:Debug>:YES>)
- target_sources(mbgl-core
- # Loop
- PRIVATE platform/darwin/src/async_task.cpp
- PRIVATE platform/darwin/src/run_loop.cpp
- PRIVATE platform/darwin/src/timer.cpp
+ target_compile_options(${target}
+ PRIVATE -fobjc-arc
+ )
+endmacro()
- # File source
- PRIVATE platform/darwin/src/http_file_source.mm
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
+include(cmake/loop-darwin.cmake)
+initialize_ios_target(mbgl-loop-darwin)
+
+
+macro(mbgl_platform_core)
+ initialize_ios_target(mbgl-core)
+
+ target_sources(mbgl-core
# Default styles
PRIVATE platform/default/mbgl/util/default_styles.hpp
- PRIVATE platform/default/mbgl/util/default_styles.cpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
- PRIVATE platform/default/sqlite3.hpp
# Misc
PRIVATE platform/darwin/mbgl/storage/reachability.h
PRIVATE platform/darwin/mbgl/storage/reachability.m
+ PRIVATE platform/darwin/src/CFHandle.hpp
+ PRIVATE platform/darwin/src/local_glyph_rasterizer.mm
PRIVATE platform/darwin/src/logging_nslog.mm
PRIVATE platform/darwin/src/nsthread.mm
PRIVATE platform/darwin/src/string_nsstring.mm
PRIVATE platform/default/bidi.cpp
+ PRIVATE platform/default/thread_local.cpp
PRIVATE platform/default/utf.cpp
# Image handling
@@ -49,13 +43,15 @@ macro(mbgl_platform_core)
PRIVATE platform/default/png_writer.cpp
# Headless view
+ PRIVATE platform/default/mbgl/gl/headless_frontend.cpp
+ 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/darwin/src/headless_backend_eagl.mm
- PRIVATE platform/default/mbgl/gl/headless_display.cpp
- PRIVATE platform/default/mbgl/gl/headless_display.hpp
- PRIVATE platform/default/mbgl/gl/offscreen_view.cpp
- PRIVATE platform/default/mbgl/gl/offscreen_view.hpp
+
+ # Snapshotting
+ PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
+ PRIVATE platform/default/mbgl/map/map_snapshotter.hpp
# Thread pool
PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp
@@ -65,19 +61,9 @@ macro(mbgl_platform_core)
)
target_add_mason_package(mbgl-core PUBLIC geojson)
+ target_add_mason_package(mbgl-core PUBLIC polylabel)
target_add_mason_package(mbgl-core PRIVATE icu)
- target_compile_options(mbgl-core
- PRIVATE -fobjc-arc
- )
-
- # 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
@@ -86,11 +72,29 @@ macro(mbgl_platform_core)
target_link_libraries(mbgl-core
PUBLIC "-lz"
PUBLIC "-framework Foundation"
+ PUBLIC "-framework CoreText"
PUBLIC "-framework CoreGraphics"
PUBLIC "-framework OpenGLES"
PUBLIC "-framework ImageIO"
PUBLIC "-framework MobileCoreServices"
PUBLIC "-framework SystemConfiguration"
+ )
+endmacro()
+
+
+macro(mbgl_filesource)
+ initialize_ios_target(mbgl-filesource)
+
+ target_sources(mbgl-filesource
+ # File source
+ PRIVATE platform/darwin/src/http_file_source.mm
+
+ # Database
+ PRIVATE platform/default/sqlite3.cpp
+ )
+
+ target_link_libraries(mbgl-filesource
PUBLIC "-lsqlite3"
+ PUBLIC "-framework Foundation"
)
endmacro()
diff --git a/platform/ios/docs/doc-README.md b/platform/ios/docs/doc-README.md
index 6c2693cbbf..7cd0376d07 100644
--- a/platform/ios/docs/doc-README.md
+++ b/platform/ios/docs/doc-README.md
@@ -1,9 +1,9 @@
-# [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)
+# [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/)
-The Mapbox iOS SDK 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 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.
-![Mapbox iOS SDK screenshots](img/screenshot.png)
+![Mapbox Maps SDK for iOS screenshots](img/screenshot.png)
-For setup information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/). For detailed usage instructions, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/). A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) is also available.
+For setup information, check out the [Mapbox Maps SDK for iOS homepage](https://www.mapbox.com/ios-sdk/). For detailed usage instructions, read “[First steps with the Mapbox Maps SDK for iOS](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/). A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) is also available.
-If you have any questions, please [contact our support team](https://www.mapbox.com/contact/). We welcome your [bug reports and feature requests](https://github.com/mapbox/mapbox-gl-native/issues/).
+If you have any questions, please see [our help page](https://www.mapbox.com/help/). We welcome your [bug reports, feature requests, and contributions](https://github.com/mapbox/mapbox-gl-native/issues/).
diff --git a/platform/ios/docs/guides/Adding Points to a Map.md b/platform/ios/docs/guides/Adding Points to a Map.md
index 2698d5564f..2844075cc7 100644
--- a/platform/ios/docs/guides/Adding Points to a Map.md
+++ b/platform/ios/docs/guides/Adding Points to a Map.md
@@ -51,7 +51,7 @@ To use annotation views, implement `MGLMapViewDelegate` `-mapView:viewForAnnotat
* 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](https://www.mapbox.com/help/switch-mapkit/#annotations-pins)
+* Familiar API for MapKit users
**Cons**
@@ -63,7 +63,7 @@ To use annotation views, implement `MGLMapViewDelegate` `-mapView:viewForAnnotat
For absolute full control of how points are displayed on a map, consider [runtime styling](runtime-styling.html).
-You can use `MGLPointFeature` or any of the other [style feature subclasses](Style%20Features.html) to add points and shapes to an `MGLShapeSource`.
+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)).
diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md
index 8b7bc05f2c..7eabfed777 100644
--- a/platform/ios/docs/guides/For Style Authors.md
+++ b/platform/ios/docs/guides/For Style Authors.md
@@ -76,6 +76,8 @@ gestures.
For more information about user interface design, consult Apple’s
[_iOS Human Interface Guidelines_](https://developer.apple.com/ios/human-interface-guidelines/).
+To learn more about designing maps for mobile devices, see [Nathaniel Slaughter's blog post](https://www.mapbox.com/blog/designing-maps-for-mobile-devices/) on
+the subject.
## Applying your style
diff --git a/platform/ios/docs/guides/Gesture Recognizers.md b/platform/ios/docs/guides/Gesture Recognizers.md
index 08e4c150e1..26237e3cfa 100644
--- a/platform/ios/docs/guides/Gesture Recognizers.md
+++ b/platform/ios/docs/guides/Gesture Recognizers.md
@@ -1,6 +1,6 @@
# User Interactions
-The Mapbox iOS SDK provides a set of built-in gesture recognizers. You can customize or supplement these gestures according to your use case. You see what gesture recognizers are on your `MGLMapView` by accessing the `gestureRecognizers` property on your map.
+The Mapbox Maps SDK for iOS provides a set of built-in gesture recognizers. You can customize or supplement these gestures according to your use case. You see what gesture recognizers are on your `MGLMapView` by accessing the `gestureRecognizers` property on your map.
## Configuring user interaction
diff --git a/platform/ios/docs/guides/Info.plist Keys.md b/platform/ios/docs/guides/Info.plist Keys.md
index c5c7cf1d85..bc2f3f5786 100644
--- a/platform/ios/docs/guides/Info.plist Keys.md
+++ b/platform/ios/docs/guides/Info.plist Keys.md
@@ -1,6 +1,6 @@
# Info.plist Keys
-The Mapbox iOS SDK supports custom `Info.plist` keys in your application in order to configure various settings.
+The Mapbox Maps SDK for iOS supports custom `Info.plist` keys in your application in order to configure various settings.
## MGLMapboxAccessToken
@@ -19,3 +19,7 @@ The default value is `https://api.mapbox.com`.
## MGLMapboxMetricsEnabledSettingShownInApp
If you have implemented custom opt-out of Mapbox Telemetry within the user interface of your app, use this key to disable the built-in check for opt-out support. See [this guide](https://www.mapbox.com/ios-sdk/#telemetry_opt_out) for more details.
+
+## MGLIdeographicFontFamilyName
+
+The name of the font family to use for client-side text rendering of CJK ideographs. Set this to the name of a font family which will be available at run time, e.g. `PingFang TC` (iOS 9+), `Heiti TC` (iOS 8+), another appropriate built-in font, or a font provided by your application. Note that if a non-existent font is specified, iOS will fall back to using Helvetica which is likely not to include support for the glyphs needed to render maps in your application.
diff --git a/platform/ios/docs/pod-README.md b/platform/ios/docs/pod-README.md
index 0a7edc5a41..f94073bd9f 100644
--- a/platform/ios/docs/pod-README.md
+++ b/platform/ios/docs/pod-README.md
@@ -1,16 +1,16 @@
-# [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)
+# [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/)
-The Mapbox iOS SDK 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 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.
-For more information, check out the [Mapbox iOS SDK 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.
+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.
[![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/docs/img/screenshot.png)]()
## Installation
-The Mapbox iOS SDK 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/).)
+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 iOS SDK 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. 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.
{{DYNAMIC}}
@@ -94,6 +94,6 @@ class ViewController: UIViewController {
}
```
-Full API documentation is included in this package, within the `documentation` folder. For more details, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/).
+Full API documentation is included in this package, within the `documentation` folder. For more details, read “[First steps with the Mapbox Maps SDK for iOS](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/).
-If you have any questions, please [contact our support team](https://www.mapbox.com/contact/). We welcome your [bug reports and feature requests](https://github.com/mapbox/mapbox-gl-native/issues/).
+If you have any questions, please see [our help page](https://www.mapbox.com/help/). We welcome your [bug reports, feature requests, and contributions](https://github.com/mapbox/mapbox-gl-native/issues/).
diff --git a/platform/ios/framework/Settings.bundle/bg.lproj/Root.strings b/platform/ios/framework/Settings.bundle/bg.lproj/Root.strings
new file mode 100644
index 0000000000..c86decde32
--- /dev/null
+++ b/platform/ios/framework/Settings.bundle/bg.lproj/Root.strings
@@ -0,0 +1,3 @@
+"TELEMETRY_GROUP_TITLE" = "Настройки за поверителност";
+"TELEMETRY_SWITCH_TITLE" = "Mapbox Телеметрия";
+"TELEMETRY_GROUP_FOOTER" = "Тази настройка позволява на приложението да споделя анонимни локации и данни за използване с Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/hu.lproj/Root.strings b/platform/ios/framework/Settings.bundle/hu.lproj/Root.strings
new file mode 100644
index 0000000000..3d761f2b97
--- /dev/null
+++ b/platform/ios/framework/Settings.bundle/hu.lproj/Root.strings
@@ -0,0 +1,3 @@
+"TELEMETRY_GROUP_TITLE" = "Adatvédelmi beállítások";
+"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetria";
+"TELEMETRY_GROUP_FOOTER" = "Ez a beállítás megengedi az alkalmazásnak, hogy névtelen helyzeti és használati adatokat osszon meg a Mapbox-szal.";
diff --git a/platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings b/platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings
index 13a8d57020..c6dc565d50 100644
--- a/platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/zh-Hant.lproj/Root.strings
@@ -1,3 +1,3 @@
"TELEMETRY_GROUP_TITLE" = "隱私設置";
-"TELEMETRY_SWITCH_TITLE" = "Mapbox傳感數據";
-"TELEMETRY_GROUP_FOOTER" = "此設置允許應用將用戶位置和數據以匿名的方式分享給Mapbox。";
+"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 a906c4fd77..a6b3372eb7 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -12,6 +12,14 @@
071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
071BBB071EE77631001FB02A /* MGLImageSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */; };
+ 0778DD431F67556700A73B34 /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; };
+ 07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */; };
+ 07D8C6FC1F67560400381808 /* MGLAbstractShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */; };
+ 07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */; };
+ 07D947521F67488800E37934 /* MGLAbstractShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 07D947531F67488E00E37934 /* MGLAbstractShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */; };
+ 07D947541F67489200E37934 /* MGLAbstractShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */; };
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, ); }; };
@@ -146,10 +154,13 @@
35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; };
35E79F211D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; };
36F1153D1D46080700878E1A /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 36F1153B1D46080700878E1A /* libmbgl-core.a */; };
+ 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */; };
+ 3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */; };
+ 3EA9363147E77DD29FA06063 /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */; };
+ 3EA9366247780E4F252652A8 /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */; };
400533011DB0862B0069F638 /* NSArray+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */; };
400533021DB0862B0069F638 /* NSArray+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */; };
400533031DB086490069F638 /* NSArray+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */; };
- 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
4018B1C91CDC288A00F666AF /* MGLAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */; };
4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -168,12 +179,12 @@
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.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert.der */; };
- 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust.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 */; };
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.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert.der */; };
- 408982EB1DEE209100754016 /* api_mapbox_com-geotrust.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust.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 */; };
@@ -181,12 +192,17 @@
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 */; };
40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; };
40F887711D7A1E59008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; };
40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; };
+ 5549A0381EF1D86B00073113 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5549A0371EF1D86B00073113 /* libmbgl-core.a */; };
5549A0391EF2877100073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; };
5549A03A1EF2877500073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; };
556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -196,26 +212,38 @@
558DE7A11E5615E400C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */; };
558DE7A21E5615E400C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE79F1E5615E400C7916D /* MGLFoundation.mm */; };
558DE7A31E5615E400C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE79F1E5615E400C7916D /* MGLFoundation.mm */; };
+ 55D120A61F791007004B6D81 /* libmbgl-loop-darwin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */; };
+ 55D120A81F79100C004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */; };
+ 55D120AA1F791015004B6D81 /* libmbgl-filesource.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */; };
+ 55D120AC1F791018004B6D81 /* libmbgl-loop-darwin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */; };
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 */; };
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, ); }; };
+ 927FBD001F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */ = {isa = PBXBuildFile; fileRef = 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 927FBD011F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */; };
+ 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 */; };
960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; };
960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 960D0C351ECF5AAF008E151F /* Images.xcassets */; };
9620BB381E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; };
9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; };
9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; };
9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; };
+ 9654C1261FFC1AB900DB6A19 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */; };
+ 9654C1291FFC1CCD00DB6A19 /* MGLPolygon_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */; };
+ 966FCF4C1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */; };
+ 966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */; };
+ 966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */; };
+ 966FCF531F3C322400F2B6DE /* MGLUserLocationHeadingArrowLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FCF501F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.h */; };
+ 966FCF541F3C323300F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 966FCF511F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.m */; };
+ 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 */; };
+ 96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; };
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 */; };
@@ -231,7 +259,6 @@
DA1DC9971CB6E046006E619F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC9961CB6E046006E619F /* main.m */; };
DA1DC9991CB6E054006E619F /* MBXAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */; };
DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA1DC99A1CB6E064006E619F /* MBXViewController.m */; };
- DA1DC99D1CB6E076006E619F /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */; };
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 */; };
@@ -267,10 +294,15 @@
DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
DA35D0881E1A6309007DED41 /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; };
+ DA5DB12A1FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */; };
DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA6408DC1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */; };
DA6408DE1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */; };
+ DA704CC21F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */; };
+ DA704CC31F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */ = {isa = PBXBuildFile; fileRef = DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */; };
+ DA704CC41F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */; };
+ DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */; };
DA72620B1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7262091DEEE3480043BB89 /* MGLOpenGLStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA72620C1DEEE3480043BB89 /* MGLOpenGLStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7262091DEEE3480043BB89 /* MGLOpenGLStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA72620A1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm */; };
@@ -546,6 +578,12 @@
071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLImageSource.h; sourceTree = "<group>"; };
071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLImageSource.mm; sourceTree = "<group>"; };
071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLImageSourceTests.m; path = ../../darwin/test/MGLImageSourceTests.m; sourceTree = "<group>"; };
+ 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = "<group>"; };
+ 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = "<group>"; };
+ 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLComputedShapeSourceTests.m; path = ../../darwin/test/MGLComputedShapeSourceTests.m; sourceTree = "<group>"; };
+ 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource_Private.h; sourceTree = "<group>"; };
+ 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource.h; sourceTree = "<group>"; };
+ 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAbstractShapeSource.mm; sourceTree = "<group>"; };
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>"; };
@@ -630,12 +668,14 @@
35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayer_Private.h; sourceTree = "<group>"; };
36F1153B1D46080700878E1A /* libmbgl-core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-core.a"; path = "build/Debug-iphoneos/libmbgl-core.a"; sourceTree = "<group>"; };
36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-platform-ios.a"; path = "build/Debug-iphoneos/libmbgl-platform-ios.a"; sourceTree = "<group>"; };
+ 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRendererConfiguration.mm; sourceTree = "<group>"; };
+ 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererConfiguration.h; sourceTree = "<group>"; };
400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MGLAdditions.h"; sourceTree = "<group>"; };
400533001DB0862B0069F638 /* NSArray+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSArray+MGLAdditions.mm"; sourceTree = "<group>"; };
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>"; };
4049C29B1DB6CD6C00B3F799 /* MGLPointCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection.h; sourceTree = "<group>"; };
@@ -645,8 +685,8 @@
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.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-digicert.der"; sourceTree = "<group>"; };
- 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_com-geotrust.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>"; };
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>"; };
@@ -654,35 +694,49 @@
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>"; };
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>"; };
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; };
+ 5549A0371EF1D86B00073113 /* libmbgl-core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-core.a"; path = "../../build/ios/Debug-iphonesimulator/libmbgl-core.a"; sourceTree = "<group>"; };
556660C91E1BF3A900E2C41B /* MGLFoundation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLFoundation.h; sourceTree = "<group>"; };
556660D71E1D085500E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = "<group>"; };
558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFoundation_Private.h; sourceTree = "<group>"; };
558DE79F1E5615E400C7916D /* MGLFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFoundation.mm; sourceTree = "<group>"; };
+ 55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-loop-darwin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-loop-darwin.a"; sourceTree = BUILT_PRODUCTS_DIR; };
55D8C9941D0F133500F42F10 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/ios/config.xcconfig; sourceTree = "<group>"; };
55D8C9951D0F18CE00F42F10 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
55E2AD121E5B125400E8C587 /* MGLOfflineStorageTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLOfflineStorageTests.mm; path = ../../darwin/test/MGLOfflineStorageTests.mm; sourceTree = "<group>"; };
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>"; };
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>"; };
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; lastKnownFileType = sourcecode.c.h; path = MGLPolyline_Private.h; sourceTree = "<group>"; };
+ 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLPolygon_Private.h; sourceTree = "<group>"; };
9660916B1E5BBFD700A9A03B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916C1E5BBFD900A9A03B /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916D1E5BBFDB00A9A03B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916E1E5BBFDC00A9A03B /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
9660916F1E5BBFDE00A9A03B /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
+ 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingBeamLayer.h; sourceTree = "<group>"; };
+ 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationHeadingBeamLayer.m; sourceTree = "<group>"; };
+ 966FCF501F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingArrowLayer.h; sourceTree = "<group>"; };
+ 966FCF511F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLUserLocationHeadingArrowLayer.m; sourceTree = "<group>"; };
968F36B41E4D0FC6003A5522 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
96E027241E57C76E004B8E66 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
96E027271E57C77A004B8E66 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -693,6 +747,7 @@
96E0272C1E57C7E5004B8E66 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
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>"; };
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>"; };
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFeatureTests.mm; path = ../../darwin/test/MGLFeatureTests.mm; sourceTree = "<group>"; };
@@ -714,7 +769,6 @@
DA1DC9961CB6E046006E619F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXAppDelegate.m; sourceTree = "<group>"; };
DA1DC99A1CB6E064006E619F /* MBXViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBXViewController.m; sourceTree = "<group>"; };
- DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; 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>"; };
@@ -731,6 +785,14 @@
DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflinePackTests.m; path = ../../darwin/test/MGLOfflinePackTests.m; sourceTree = "<group>"; };
DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLOfflineRegionTests.m; path = ../../darwin/test/MGLOfflineRegionTests.m; sourceTree = "<group>"; };
DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLStyleTests.mm; path = ../../darwin/test/MGLStyleTests.mm; sourceTree = "<group>"; };
+ DA33895F1FA3EAB7001EA329 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Foundation.strings"; sourceTree = "<group>"; };
+ DA3389651FA3EE1B001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA3389661FA3EE28001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA3389671FA3EE2F001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
+ DA3389681FA3EE48001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA3389691FA3EE50001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+ DA33896A1FA3EE58001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Root.strings; sourceTree = "<group>"; };
+ DA33896B1FA3EF4A001EA329 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA35A29D1CC9E94C00E826B2 /* MGLCoordinateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCoordinateFormatter.h; sourceTree = "<group>"; };
DA35A2A01CC9E95F00E826B2 /* MGLCoordinateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCoordinateFormatter.m; sourceTree = "<group>"; };
DA35A2A91CCA058D00E826B2 /* MGLCoordinateFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCoordinateFormatterTests.m; path = ../../darwin/test/MGLCoordinateFormatterTests.m; sourceTree = "<group>"; };
@@ -749,13 +811,16 @@
DA57D4AA1EBA8ED300793288 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA57D4AB1EBA909900793288 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA57D4AC1EBA922A00793288 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+ DA5C09BA1EFC48550056B178 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA5C09BB1EFC486C0056B178 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapAccessibilityElementTests.m; sourceTree = "<group>"; };
DA6023F11E4CE94300DBFF23 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Foundation.strings; sourceTree = "<group>"; };
DA6023F21E4CE94800DBFF23 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B111E68823600CB7F44 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA618B191E68883700CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ca; path = ca.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA618B1A1E68883900CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B1B1E68884E00CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
- DA618B1C1E6888EC00CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA618B1C1E6888EC00CB7F44 /* ca */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Foundation.strings; sourceTree = "<group>"; };
DA618B1D1E6888F500CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ca; path = ca.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B1E1E688A3700CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Root.strings; sourceTree = "<group>"; };
DA618B251E68920500CB7F44 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Foundation.strings; sourceTree = "<group>"; };
@@ -764,6 +829,12 @@
DA618B2C1E68933600CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Root.strings; sourceTree = "<group>"; };
DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorStyleLayer.h; sourceTree = "<group>"; };
DA6408DA1DA4E7D300908C90 /* MGLVectorStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLVectorStyleLayer.m; sourceTree = "<group>"; };
+ DA704CBB1F637311004B3F28 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA704CBC1F637405004B3F28 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = uk; path = uk.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+ DA704CBD1F63746E004B3F28 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
+ DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapAccessibilityElement.h; sourceTree = "<group>"; };
+ DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapAccessibilityElement.mm; sourceTree = "<group>"; };
+ DA704CC71F6663A3004B3F28 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Foundation.strings; sourceTree = "<group>"; };
DA7262091DEEE3480043BB89 /* MGLOpenGLStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOpenGLStyleLayer.h; sourceTree = "<group>"; };
DA72620A1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOpenGLStyleLayer.mm; sourceTree = "<group>"; };
DA737ADA1E59139D00AD2CDE /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
@@ -879,7 +950,7 @@
DAA32CAC1E4C4971006F8D24 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CB11E4C4C8A006F8D24 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Root.strings; sourceTree = "<group>"; };
DAA32CB51E4C4CF4006F8D24 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Foundation.strings; sourceTree = "<group>"; };
- DAA32CB71E4C4ED8006F8D24 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DAA32CB71E4C4ED8006F8D24 /* sv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CB81E4C4EE6006F8D24 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Root.strings; sourceTree = "<group>"; };
DAA32CBC1E4C4F5D006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CBD1E4C4F62006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Foundation.strings; sourceTree = "<group>"; };
@@ -903,6 +974,8 @@
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>"; };
DAC49C621CD07D74009E1AA3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; 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>"; };
DAD1656A1CF41981001FF4B9 /* MGLFeature_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFeature_Private.h; sourceTree = "<group>"; };
DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = "<group>"; };
@@ -918,8 +991,8 @@
DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterSource_Private.h; sourceTree = "<group>"; };
DAF0D8121DFE0EC500B28378 /* MGLVectorSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorSource_Private.h; sourceTree = "<group>"; };
DAF0D8171DFE6B2800B28378 /* MGLAttributionInfo_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo_Private.h; sourceTree = "<group>"; };
- DAFBD0D21E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = "<group>"; };
- DAFBD0D31E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; 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>"; };
DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = "<group>"; };
DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = "<group>"; };
@@ -948,6 +1021,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 5549A0381EF1D86B00073113 /* libmbgl-core.a in Frameworks */,
DA2E88561CC036F400F24E7B /* Mapbox.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -957,6 +1031,8 @@
buildActionMask = 2147483647;
files = (
DAABF73D1CBC59BB005B1825 /* libmbgl-core.a in Frameworks */,
+ 55D120A61F791007004B6D81 /* libmbgl-loop-darwin.a in Frameworks */,
+ 55D120A81F79100C004B6D81 /* libmbgl-filesource.a in Frameworks */,
DA27C24E1CBB3811000B0ECD /* GLKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -966,6 +1042,8 @@
buildActionMask = 2147483647;
files = (
36F1153D1D46080700878E1A /* libmbgl-core.a in Frameworks */,
+ 55D120AC1F791018004B6D81 /* libmbgl-loop-darwin.a in Frameworks */,
+ 55D120AA1F791015004B6D81 /* libmbgl-filesource.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -984,6 +1062,11 @@
35136D491D4277EA00C20EFD /* Sources */ = {
isa = PBXGroup;
children = (
+ 07D9474F1F67487E00E37934 /* MGLAbstractShapeSource.h */,
+ 07D9474E1F67487E00E37934 /* MGLAbstractShapeSource_Private.h */,
+ 07D947501F67487E00E37934 /* MGLAbstractShapeSource.mm */,
+ 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */,
+ 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */,
071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */,
071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */,
3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */,
@@ -1143,6 +1226,7 @@
40CFA64E1D78754A008103BD /* Sources */ = {
isa = PBXGroup;
children = (
+ 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */,
071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */,
40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */,
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */,
@@ -1151,6 +1235,20 @@
name = Sources;
sourceTree = "<group>";
};
+ 9604FC341F313A5E003EEA02 /* Fixtures */ = {
+ isa = PBXGroup;
+ children = (
+ 353BAEF51D646370009A8DA9 /* amsterdam.geojson */,
+ DA1DC96C1CB6C6CE006E619F /* points.geojson */,
+ DA1DC96D1CB6C6CE006E619F /* polyline.geojson */,
+ DA1DC96F1CB6C6CE006E619F /* threestates.geojson */,
+ DD4823721D94AE6C00EB71B7 /* fill_filter_style.json */,
+ DD4823731D94AE6C00EB71B7 /* line_filter_style.json */,
+ DD4823741D94AE6C00EB71B7 /* numeric_filter_style.json */,
+ );
+ name = Fixtures;
+ sourceTree = "<group>";
+ };
DA1DC9411CB6C1C2006E619F = {
isa = PBXGroup;
children = (
@@ -1192,6 +1290,8 @@
354B839B1D2E9B48005D9406 /* MBXUserLocationAnnotationView.m */,
DA1DC9681CB6C6B7006E619F /* MBXOfflinePacksTableViewController.h */,
DA1DC9691CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m */,
+ 927FBCFA1F4DAA8300F8BF1F /* MBXSnapshotsViewController.h */,
+ 927FBCFB1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m */,
DA1DC9531CB6C1C2006E619F /* MBXViewController.h */,
DA1DC99A1CB6E064006E619F /* MBXViewController.m */,
632281DD1E6F855900D75A5D /* MBXEmbeddedMapViewController.h */,
@@ -1199,16 +1299,9 @@
DA821D051CCC6D59007508D4 /* Main.storyboard */,
DA821D041CCC6D59007508D4 /* LaunchScreen.storyboard */,
DA1DC99E1CB6E088006E619F /* Assets.xcassets */,
- DA1DC96C1CB6C6CE006E619F /* points.geojson */,
- DA1DC96D1CB6C6CE006E619F /* polyline.geojson */,
- DA1DC96F1CB6C6CE006E619F /* threestates.geojson */,
- 353BAEF51D646370009A8DA9 /* amsterdam.geojson */,
- DD4823721D94AE6C00EB71B7 /* fill_filter_style.json */,
- DD4823731D94AE6C00EB71B7 /* line_filter_style.json */,
- DD4823741D94AE6C00EB71B7 /* numeric_filter_style.json */,
DA1DC95E1CB6C1C2006E619F /* Info.plist */,
- DA1DC99C1CB6E076006E619F /* Default-568h@2x.png */,
96E027251E57C76E004B8E66 /* Localizable.strings */,
+ 9604FC341F313A5E003EEA02 /* Fixtures */,
DA1DC94D1CB6C1C2006E619F /* Supporting Files */,
);
name = "Demo App";
@@ -1226,6 +1319,11 @@
DA1DC9921CB6DF24006E619F /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */,
+ 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */,
+ 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */,
+ 55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */,
+ 5549A0371EF1D86B00073113 /* libmbgl-core.a */,
36F1153B1D46080700878E1A /* libmbgl-core.a */,
36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */,
554180411D2E97DE00012372 /* OpenGLES.framework */,
@@ -1268,6 +1366,7 @@
3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */,
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */,
DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */,
+ DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */,
35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */,
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */,
DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */,
@@ -1310,6 +1409,7 @@
DAD165801CF4CF9A001FF4B9 /* Formatters */,
DAD165811CF4CFC4001FF4B9 /* Geometry */,
DAD165821CF4CFE3001FF4B9 /* Offline Maps */,
+ DA8848911CBB049300AB86E3 /* reachability */,
DA8847DF1CBAFA5100AB86E3 /* MGLAccountManager.h */,
DA8847FF1CBAFA6200AB86E3 /* MGLAccountManager_Private.h */,
DA8848001CBAFA6200AB86E3 /* MGLAccountManager.m */,
@@ -1321,14 +1421,18 @@
558DE79F1E5615E400C7916D /* MGLFoundation.mm */,
DA8847E21CBAFA5100AB86E3 /* MGLMapCamera.h */,
DA8848031CBAFA6200AB86E3 /* MGLMapCamera.mm */,
+ 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */,
+ 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */,
DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */,
DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */,
+ 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */,
+ 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */,
+ 92F2C3EC1F0E3C3A00268EC0 /* MGLRendererFrontend.h */,
DA8847EC1CBAFA5100AB86E3 /* MGLStyle.h */,
35E0CFE51D3E501500188327 /* MGLStyle_Private.h */,
DA88480F1CBAFA6200AB86E3 /* MGLStyle.mm */,
DA8847EE1CBAFA5100AB86E3 /* MGLTypes.h */,
DA8848111CBAFA6200AB86E3 /* MGLTypes.m */,
- DA8848911CBB049300AB86E3 /* reachability */,
35E1A4D71D74336F007AA97F /* MGLValueEvaluator.h */,
);
name = Foundation;
@@ -1342,6 +1446,8 @@
35CE617F1D4165C2004F2359 /* Categories */,
DAD165841CF4D06B001FF4B9 /* Annotations */,
DAD165851CF4D08B001FF4B9 /* Telemetry */,
+ DA704CC01F65A475004B3F28 /* MGLMapAccessibilityElement.h */,
+ DA704CC11F65A475004B3F28 /* MGLMapAccessibilityElement.mm */,
DA8848361CBAFB8500AB86E3 /* MGLMapView.h */,
DA17BE2F1CC4BAC300402C41 /* MGLMapView_Private.h */,
DA8848371CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h */,
@@ -1362,8 +1468,10 @@
DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */,
DA8933EF1CCD387900E68420 /* strip-frameworks.sh */,
40599F001DEE1B2400182B5D /* api_mapbox_staging.der */,
- 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert.der */,
- 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust.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;
@@ -1505,8 +1613,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 */,
@@ -1537,10 +1647,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 */,
@@ -1575,8 +1681,8 @@
DAD165841CF4D06B001FF4B9 /* Annotations */ = {
isa = PBXGroup;
children = (
- 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */,
404326881D5B9B1A007111BD /* MGLAnnotationContainerView_Private.h */,
+ 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */,
40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */,
DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */,
DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */,
@@ -1595,6 +1701,11 @@
359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */,
354B83941D2E873E005D9406 /* MGLUserLocationAnnotationView.h */,
354B83951D2E873E005D9406 /* MGLUserLocationAnnotationView.m */,
+ 966FCF501F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.h */,
+ 966FCF511F3C321000F2B6DE /* MGLUserLocationHeadingArrowLayer.m */,
+ 966FCF4A1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h */,
+ 966FCF4B1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m */,
+ 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */,
);
name = Annotations;
sourceTree = "<group>";
@@ -1635,8 +1746,9 @@
35E1A4D81D74336F007AA97F /* MGLValueEvaluator.h in Headers */,
DA88482C1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h in Headers */,
357FE2DD1E02D2B20068B753 /* NSCoder+MGLAdditions.h in Headers */,
- 7E016D7E1D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.h in Headers */,
35D13AB71D3D15E300AFB4E0 /* MGLStyleLayer.h in Headers */,
+ 07D947531F67488E00E37934 /* MGLAbstractShapeSource_Private.h in Headers */,
+ 9654C1261FFC1AB900DB6A19 /* MGLPolyline_Private.h in Headers */,
DA88488E1CBB047F00AB86E3 /* reachability.h in Headers */,
40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */,
350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */,
@@ -1650,18 +1762,20 @@
DA8847FB1CBAFA5100AB86E3 /* MGLShape.h in Headers */,
353933F51D3FB785003F57D7 /* MGLBackgroundStyleLayer.h in Headers */,
DA88485A1CBAFB9800AB86E3 /* MGLUserLocation_Private.h in Headers */,
+ 966FCF531F3C322400F2B6DE /* MGLUserLocationHeadingArrowLayer.h in Headers */,
DA27C24F1CBB4C11000B0ECD /* MGLAccountManager_Private.h in Headers */,
+ 07D947521F67488800E37934 /* MGLAbstractShapeSource.h in Headers */,
DA8847FC1CBAFA5100AB86E3 /* MGLStyle.h in Headers */,
DD9BE4F71EB263C50079A3AF /* UIViewController+MGLAdditions.h in Headers */,
DAF0D8131DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */,
354B83961D2E873E005D9406 /* MGLUserLocationAnnotationView.h in Headers */,
DA8847F01CBAFA5100AB86E3 /* MGLAnnotation.h in Headers */,
- 7E016D841D9E890300A29A21 /* MGLPolygon+MGLAdditions.h in Headers */,
400533011DB0862B0069F638 /* NSArray+MGLAdditions.h in Headers */,
1F06668A1EC64F8E001C16D7 /* MGLLight.h in Headers */,
4049C29D1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */,
40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */,
4018B1CA1CDC288E00F666AF /* MGLAnnotationView.h in Headers */,
+ 9654C1291FFC1CCD00DB6A19 /* MGLPolygon_Private.h in Headers */,
35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */,
FA68F14A1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */,
353933FB1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */,
@@ -1670,19 +1784,24 @@
DA35A2C91CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
3510FFEA1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h in Headers */,
DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */,
+ DA704CC21F65A475004B3F28 /* MGLMapAccessibilityElement.h in Headers */,
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 */,
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 */,
558DE7A01E5615E400C7916D /* MGLFoundation_Private.h in Headers */,
+ 966FCF4C1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.h in Headers */,
DA88483D1CBAFB8500AB86E3 /* MGLMapView+IBAdditions.h in Headers */,
DA17BE301CC4BAC300402C41 /* MGLMapView_Private.h in Headers */,
DAD165781CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */,
@@ -1691,6 +1810,7 @@
3566C7661D4A77BA008152BC /* MGLShapeSource.h in Headers */,
35CE61821D4165D9004F2359 /* UIColor+MGLAdditions.h in Headers */,
35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */,
+ 927FBCFF1F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */,
DA35A29E1CC9E94C00E826B2 /* MGLCoordinateFormatter.h in Headers */,
DAF0D8181DFE6B2800B28378 /* MGLAttributionInfo_Private.h in Headers */,
DAAF722B1DA903C700312FA4 /* MGLStyleValue.h in Headers */,
@@ -1738,6 +1858,7 @@
DA8847F61CBAFA5100AB86E3 /* MGLOfflineStorage.h in Headers */,
DAD1656E1CF41981001FF4B9 /* MGLFeature_Private.h in Headers */,
DA88483C1CBAFB8500AB86E3 /* MGLMapView.h in Headers */,
+ 3EA9363147E77DD29FA06063 /* MGLRendererConfiguration.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1767,6 +1888,7 @@
35E0CFE71D3E501500188327 /* MGLStyle_Private.h in Headers */,
DABFB86D1CBE9A0F00D62B32 /* MGLAnnotationImage.h in Headers */,
DABFB8721CBE9A0F00D62B32 /* MGLUserLocation.h in Headers */,
+ 927FBD001F4DB05500F8BF1F /* MGLMapSnapshotter.h in Headers */,
3566C7721D4A9198008152BC /* MGLSource_Private.h in Headers */,
353933FF1D3FB7DD003F57D7 /* MGLSymbolStyleLayer.h in Headers */,
DAAF722E1DA903C700312FA4 /* MGLStyleValue_Private.h in Headers */,
@@ -1780,7 +1902,6 @@
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 */,
353AFA151D65AB17005A69F4 /* NSDate+MGLAdditions.h in Headers */,
3510FFFA1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */,
@@ -1800,6 +1921,7 @@
558DE7A11E5615E400C7916D /* MGLFoundation_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 */,
@@ -1813,7 +1935,6 @@
968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */,
4018B1CB1CDC288E00F666AF /* MGLAnnotationView.h in Headers */,
DABFB85F1CBE99E500D62B32 /* MGLGeometry.h in Headers */,
- 7E016D851D9E890300A29A21 /* MGLPolygon+MGLAdditions.h in Headers */,
353933F61D3FB785003F57D7 /* MGLBackgroundStyleLayer.h in Headers */,
DABFB85D1CBE99E500D62B32 /* MGLAccountManager.h in Headers */,
353933F91D3FB79F003F57D7 /* MGLLineStyleLayer.h in Headers */,
@@ -1828,6 +1949,7 @@
DAF0D8191DFE6B2800B28378 /* MGLAttributionInfo_Private.h in Headers */,
DABFB86A1CBE99E500D62B32 /* MGLStyle.h in Headers */,
DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */,
+ 3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1966,7 +2088,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = MBX;
- LastUpgradeCheck = 0800;
+ LastUpgradeCheck = 0910;
ORGANIZATIONNAME = Mapbox;
TargetAttributes = {
DA1DC9491CB6C1C2006E619F = {
@@ -2018,6 +2140,8 @@
ca,
fi,
nl,
+ hu,
+ bg,
);
mainGroup = DA1DC9411CB6C1C2006E619F;
productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */;
@@ -2046,7 +2170,6 @@
353BAEF61D646370009A8DA9 /* amsterdam.geojson in Resources */,
DA1DC9711CB6C6CE006E619F /* polyline.geojson in Resources */,
DD4823761D94AE6C00EB71B7 /* line_filter_style.json in Resources */,
- DA1DC99D1CB6E076006E619F /* Default-568h@2x.png in Resources */,
DA821D071CCC6D59007508D4 /* Main.storyboard in Resources */,
DA1DC9731CB6C6CE006E619F /* threestates.geojson in Resources */,
DA821D061CCC6D59007508D4 /* LaunchScreen.storyboard in Resources */,
@@ -2085,9 +2208,11 @@
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 */,
- 408982EA1DEE208B00754016 /* api_mapbox_com-digicert.der in Resources */,
- 408982EB1DEE209100754016 /* api_mapbox_com-geotrust.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;
};
@@ -2099,10 +2224,12 @@
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.der in Resources */,
- 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust.der in Resources */,
+ 40599F0D1DEE1B7A00182B5D /* api_mapbox_com-digicert_2016.der in Resources */,
+ 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust_2016.der in Resources */,
+ 40EA6BC21EF4599700FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2131,6 +2258,7 @@
DA1DC9991CB6E054006E619F /* MBXAppDelegate.m in Sources */,
DA1DC96B1CB6C6B7006E619F /* MBXOfflinePacksTableViewController.m in Sources */,
DA1DC96A1CB6C6B7006E619F /* MBXCustomCalloutView.m in Sources */,
+ 927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */,
DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */,
40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */,
632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */,
@@ -2171,7 +2299,9 @@
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 */,
@@ -2189,7 +2319,6 @@
files = (
35136D391D42271A00C20EFD /* MGLBackgroundStyleLayer.mm in Sources */,
3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */,
- 7E016D801D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */,
DAED38651D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */,
9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */,
354B83981D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */,
@@ -2199,6 +2328,7 @@
40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */,
DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */,
DA8848251CBAFA6200AB86E3 /* MGLPointAnnotation.mm in Sources */,
+ 929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */,
35136D3C1D42272500C20EFD /* MGLCircleStyleLayer.mm in Sources */,
DD9BE4F81EB263C50079A3AF /* UIViewController+MGLAdditions.m in Sources */,
350098DE1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */,
@@ -2209,7 +2339,9 @@
3538AA1F1D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
DA88482D1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m in Sources */,
+ 966FCF541F3C323300F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */,
DA88485B1CBAFB9800AB86E3 /* MGLUserLocation.m in Sources */,
+ 927FBD011F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */,
350098BD1D480108004B2AF0 /* MGLVectorSource.mm in Sources */,
3566C76E1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */,
DA88488C1CBB037E00AB86E3 /* SMCalloutView.m in Sources */,
@@ -2220,6 +2352,7 @@
35136D451D42275100C20EFD /* MGLSymbolStyleLayer.mm in Sources */,
35599DED1D46F14E0048254D /* MGLStyleValue.mm in Sources */,
DA8848211CBAFA6200AB86E3 /* MGLOfflinePack.mm in Sources */,
+ 0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */,
3557F7B21E1D27D300CCA5E6 /* MGLDistanceFormatter.m in Sources */,
DA8848591CBAFB9800AB86E3 /* MGLMapView.mm in Sources */,
DA8848501CBAFB9800AB86E3 /* MGLAnnotationImage.m in Sources */,
@@ -2240,15 +2373,15 @@
3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */,
DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */,
DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */,
- 4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
FA68F14D1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */,
404C26E41D89B877000AA13D /* MGLTileSource.mm in Sources */,
+ 07D947541F67489200E37934 /* MGLAbstractShapeSource.mm in Sources */,
355AE0011E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
DA88481D1CBAFA6200AB86E3 /* MGLMapCamera.mm in Sources */,
DA8848261CBAFA6200AB86E3 /* MGLPolygon.mm in Sources */,
35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
- 7E016D861D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */,
DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */,
+ 966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */,
353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
@@ -2256,6 +2389,7 @@
DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */,
4049C29F1DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
35136D3F1D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
+ DA704CC41F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */,
3510FFFB1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
@@ -2263,6 +2397,7 @@
DA8848581CBAFB9800AB86E3 /* MGLMapboxEvents.m in Sources */,
35CE61841D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */,
DA8848561CBAFB9800AB86E3 /* MGLLocationManager.m in Sources */,
+ 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2272,7 +2407,6 @@
files = (
35136D3A1D42271A00C20EFD /* MGLBackgroundStyleLayer.mm in Sources */,
3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */,
- 7E016D811D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */,
354B83991D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */,
9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */,
DAA4E4221CBB730400178DFB /* MGLPointAnnotation.mm in Sources */,
@@ -2292,7 +2426,9 @@
3538AA201D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
DA00FC911D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */,
+ 966FCF551F3C323500F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */,
DAA4E4331CBB730400178DFB /* MGLUserLocation.m in Sources */,
+ 927FBD021F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */,
350098BE1D480108004B2AF0 /* MGLVectorSource.mm in Sources */,
3566C76F1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */,
DAA4E4351CBB730400178DFB /* SMCalloutView.m in Sources */,
@@ -2328,17 +2464,20 @@
404C26E51D89B877000AA13D /* MGLTileSource.mm in Sources */,
355AE0021E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
+ 07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */,
DAA4E4341CBB730400178DFB /* MGLFaux3DUserLocationAnnotationView.m in Sources */,
35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
- 7E016D871D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */,
DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */,
+ 966FCF4F1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
DAA4E4231CBB730400178DFB /* MGLPolygon.mm in Sources */,
353AFA171D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */,
DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */,
4049C2A01DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
+ 07D8C6FC1F67560400381808 /* MGLAbstractShapeSource.mm in Sources */,
35136D401D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
+ DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */,
3510FFFC1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
@@ -2346,6 +2485,7 @@
DAA4E41C1CBB730400178DFB /* MGLAccountManager.m in Sources */,
35CE61851D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */,
DAA4E4241CBB730400178DFB /* MGLPolyline.mm in Sources */,
+ 3EA9366247780E4F252652A8 /* MGLRendererConfiguration.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2416,6 +2556,8 @@
DA618B1A1E68883900CB7F44 /* ca */,
DA618B2B1E68932D00CB7F44 /* fi */,
DAE8CCAD1E6E8C70009B5CB0 /* nl */,
+ DA5C09BA1EFC48550056B178 /* hu */,
+ DA3389651FA3EE1B001EA329 /* bg */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -2439,6 +2581,8 @@
DA618B1E1E688A3700CB7F44 /* ca */,
DA618B2C1E68933600CB7F44 /* fi */,
DAE8CCAE1E6E8C76009B5CB0 /* nl */,
+ DACCD9C81F1F473700BB09A1 /* hu */,
+ DA33896A1FA3EE58001EA329 /* bg */,
);
name = Root.strings;
sourceTree = "<group>";
@@ -2460,6 +2604,8 @@
DA737AE91E5917C300AD2CDE /* uk */,
DA1AC01B1E5B8774006DF1D6 /* lt */,
DA618B1B1E68884E00CB7F44 /* ca */,
+ DA5C09BB1EFC486C0056B178 /* hu */,
+ DA3389681FA3EE48001EA329 /* bg */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -2477,6 +2623,10 @@
DA618B1C1E6888EC00CB7F44 /* ca */,
DA618B251E68920500CB7F44 /* lt */,
DAE9E0F11EB7BF1B001E8E8B /* es */,
+ DA704CBB1F637311004B3F28 /* ru */,
+ DA704CC71F6663A3004B3F28 /* uk */,
+ DA33895F1FA3EAB7001EA329 /* pt-BR */,
+ DA3389661FA3EE28001EA329 /* bg */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -2495,6 +2645,9 @@
DA1AC0201E5B8917006DF1D6 /* uk */,
DA618B1D1E6888F500CB7F44 /* ca */,
DA618B261E68920D00CB7F44 /* lt */,
+ DACFE7981F66EA2100630DA8 /* vi */,
+ DA3389671FA3EE2F001EA329 /* bg */,
+ DA33896B1FA3EF4A001EA329 /* hu */,
);
name = Foundation.stringsdict;
sourceTree = "<group>";
@@ -2520,6 +2673,9 @@
DA57D4AA1EBA8ED300793288 /* es */,
DA57D4AB1EBA909900793288 /* lt */,
DA57D4AC1EBA922A00793288 /* vi */,
+ DA704CBC1F637405004B3F28 /* uk */,
+ DA704CBD1F63746E004B3F28 /* zh-Hant */,
+ DA3389691FA3EE50001EA329 /* bg */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";
@@ -2537,7 +2693,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;
@@ -2545,7 +2703,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;
@@ -2590,7 +2752,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;
@@ -2598,7 +2762,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;
@@ -2621,6 +2789,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SYMROOT = ../../build/ios;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
@@ -2680,13 +2849,11 @@
baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
- HEADER_SEARCH_PATHS = (
- "$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_INCLUDE_DIRECTORIES)",
- );
+ 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)",
@@ -2707,13 +2874,11 @@
baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
- HEADER_SEARCH_PATHS = (
- "$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_INCLUDE_DIRECTORIES)",
- );
+ 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)",
@@ -2733,6 +2898,7 @@
baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2740,12 +2906,13 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_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 = (
"$(OTHER_CFLAGS)",
"$(sqlite_cflags)",
@@ -2755,7 +2922,10 @@
"$(geometry_cflags)",
"$(geojson_cflags)",
);
- OTHER_LDFLAGS = "$(mbgl_core_LINK_LIBRARIES)";
+ OTHER_LDFLAGS = (
+ "$(mbgl_core_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
+ );
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
PRODUCT_NAME = Mapbox;
SKIP_INSTALL = YES;
@@ -2770,6 +2940,7 @@
baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */;
buildSettings = {
BITCODE_GENERATION_MODE = bitcode;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2777,12 +2948,13 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_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 = (
"$(OTHER_CFLAGS)",
"$(sqlite_cflags)",
@@ -2792,7 +2964,10 @@
"$(geometry_cflags)",
"$(geojson_cflags)",
);
- OTHER_LDFLAGS = "$(mbgl_core_LINK_LIBRARIES)";
+ OTHER_LDFLAGS = (
+ "$(mbgl_core_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
+ );
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.sdk.ios;
PRODUCT_NAME = Mapbox;
SKIP_INSTALL = YES;
@@ -2831,8 +3006,9 @@
BITCODE_GENERATION_MODE = bitcode;
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
+ OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"$(sqlite_cflags)",
@@ -2845,6 +3021,7 @@
OTHER_LDFLAGS = (
"-ObjC",
"$(mbgl_core_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
PRODUCT_NAME = Mapbox;
PUBLIC_HEADERS_FOLDER_PATH = Headers;
@@ -2860,8 +3037,9 @@
BITCODE_GENERATION_MODE = bitcode;
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
+ OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"$(sqlite_cflags)",
@@ -2874,6 +3052,7 @@
OTHER_LDFLAGS = (
"-ObjC",
"$(mbgl_core_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
PRODUCT_NAME = Mapbox;
PUBLIC_HEADERS_FOLDER_PATH = Headers;
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/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 ba56c312eb..90e357f72e 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' />
@@ -76,7 +76,9 @@ custom_categories:
- MGLSource
- MGLTileSource
- MGLImageSource
+ - MGLAbstractShapeSource
- MGLShapeSource
+ - MGLComputedShapeSource
- MGLRasterSource
- MGLVectorSource
- name: Style Layers
diff --git a/platform/ios/resources/Base.lproj/Localizable.strings b/platform/ios/resources/Base.lproj/Localizable.strings
index 3f59262d71..22ed9278bc 100644
--- a/platform/ios/resources/Base.lproj/Localizable.strings
+++ b/platform/ios/resources/Base.lproj/Localizable.strings
@@ -34,6 +34,9 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "About this map";
+/* List separator */
+"LIST_SEPARATOR" = ", ";
+
/* User-friendly error description */
"LOAD_MAP_FAILED_DESC" = "The map failed to load because an unknown error occurred.";
@@ -46,17 +49,35 @@
/* Accessibility label */
"MAP_A11Y_LABEL" = "Map";
-/* Map accessibility value */
-"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible";
+/* Map accessibility value; {number of visible annotations} */
+"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld annotation(s) visible.";
+
+/* Map accessibility value; {list of visible places} */
+"MAP_A11Y_VALUE_PLACES" = "Places visible: %@.";
+
+/* Map accessibility value; {number of visible roads} */
+"MAP_A11Y_VALUE_ROADS" = "%ld road(s) visible.";
+
+/* Map accessibility value; {zoom level} */
+"MAP_A11Y_VALUE_ZOOM" = "Zoom %dx.";
/* User-friendly error description */
"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted.";
+/* Accessibility value indicating that a road is a divided road (dual carriageway) */
+"ROAD_DIVIDED_A11Y_VALUE" = "Divided road";
+
+/* Accessibility value indicating that a road is a one-way road */
+"ROAD_ONEWAY_A11Y_VALUE" = "One way";
+
+/* String format for accessibility value for road feature; {route number} */
+"ROAD_REF_A11Y_FMT" = "Route %@";
+
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK version %@ is now available:";
+"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS version %@ is now available:";
/* User-friendly error description */
"STYLE_NOT_FOUND_DESC" = "The map failed to load because the style can’t be found or is incompatible.";
diff --git a/platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdf b/platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdf
index 4e2e332301..d3e0e2ce12 100644
--- a/platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdf
+++ b/platform/ios/resources/Images.xcassets/default_marker.imageset/default_marker.pdf
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_com-digicert.der b/platform/ios/resources/api_mapbox_com-digicert_2016.der
index e8ef427f33..e8ef427f33 100644
--- a/platform/ios/resources/api_mapbox_com-digicert.der
+++ b/platform/ios/resources/api_mapbox_com-digicert_2016.der
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
new file mode 100644
index 0000000000..4a190085ab
--- /dev/null
+++ b/platform/ios/resources/api_mapbox_com-digicert_2017.der
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_com-geotrust.der b/platform/ios/resources/api_mapbox_com-geotrust_2016.der
index 1c7331dedc..1c7331dedc 100644
--- a/platform/ios/resources/api_mapbox_com-geotrust.der
+++ b/platform/ios/resources/api_mapbox_com-geotrust_2016.der
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
new file mode 100644
index 0000000000..7bb9befbbf
--- /dev/null
+++ b/platform/ios/resources/api_mapbox_com-geotrust_2017.der
Binary files differ
diff --git a/platform/ios/resources/bg.lproj/Localizable.strings b/platform/ios/resources/bg.lproj/Localizable.strings
new file mode 100644
index 0000000000..e9b35e4438
--- /dev/null
+++ b/platform/ios/resources/bg.lproj/Localizable.strings
@@ -0,0 +1,93 @@
+/* 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" = "За тази карта";
+
+/* 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 */
+"MAP_A11Y_VALUE" = "Мащаб %1$dх\n@2$ld видима(и) анотация(и)";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Картата не се зареди поради повреден стил.";
+
+/* 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 версия %@:";
+
+/* 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 prompt title */
+"TELEMETRY_TITLE" = "Направи Mapbox картите по-добри";
+
+/* Default user location annotation title */
+"USER_DOT_TITLE" = "Сега си тук";
+
diff --git a/platform/ios/resources/bg.lproj/Localizable.stringsdict b/platform/ios/resources/bg.lproj/Localizable.stringsdict
new file mode 100644
index 0000000000..f155a02acc
--- /dev/null
+++ b/platform/ios/resources/bg.lproj/Localizable.stringsdict
@@ -0,0 +1,34 @@
+<?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</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@level@
+%#@count@</string>
+ <key>level</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>Мащаб %dx</string>
+ <key>other</key>
+ <string>Мащаб %dx</string>
+ </dict>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>one</key>
+ <string>%d видима анотация</string>
+ <key>other</key>
+ <string>%d видими анотации</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/ios/resources/ca.lproj/Localizable.strings b/platform/ios/resources/ca.lproj/Localizable.strings
index ae655f282d..a5c06f739e 100644
--- a/platform/ios/resources/ca.lproj/Localizable.strings
+++ b/platform/ios/resources/ca.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Mostra més informació";
/* No comment provided by engineer. */
@@ -53,10 +53,10 @@
"PARSE_STYLE_FAILED_DESC" = "El mapa no s’ha carregat perquè s’ha corromput l’estil.";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "La versió %@ del Mapbox iOS SDK està disponible:";
+"SDK_UPDATE_AVAILABLE" = "La versió %@ del Mapbox Maps SDK for iOS està disponible:";
/* User-friendly error description */
"STYLE_NOT_FOUND_DESC" = "El mapa no s’ha carregat perquè no es troba l’estil o bé és incompatible.";
diff --git a/platform/ios/resources/de.lproj/Localizable.strings b/platform/ios/resources/de.lproj/Localizable.strings
index 1ea03e7d61..f3e5dfe2f1 100644
--- a/platform/ios/resources/de.lproj/Localizable.strings
+++ b/platform/ios/resources/de.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Mehr Infos anzeigen";
/* No comment provided by engineer. */
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "Abbrechen";
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Zurück zur Karte ";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Dreht die Karte nach Norden";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Über diese Karte";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Die Karte konnte nicht geladen werden, da ein unbekannter Fehler aufgetreten ist.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Die Karte konnte nicht geladen werden, da diese Form nicht geladen werden kann";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -40,8 +49,17 @@
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Zoomstufe %1$d\n%2$ld Anmerkung(en) sichtbar";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Die Karte konnte nicht geladen werden, da diese Form beschädigt ist.";
+
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"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 %@ ist ab sofort verfügbar.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Die Karte konnte nicht geladen werden, da diese Form nicht gefunden werden kann oder nicht kompatibel ist.";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Durch anonymisierte Nutzungsdaten können Sie helfen, OpenStreetMap- und Mapbox-Karten zu verbessern.";
diff --git a/platform/ios/resources/en.lproj/Localizable.stringsdict b/platform/ios/resources/en.lproj/Localizable.stringsdict
index e849318fe5..435b7bdfe8 100644
--- a/platform/ios/resources/en.lproj/Localizable.stringsdict
+++ b/platform/ios/resources/en.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 visible</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 road visible</string>
<key>other</key>
- <string>%d annotations visible</string>
+ <string>%d roads visible</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>
diff --git a/platform/ios/resources/es.lproj/Localizable.strings b/platform/ios/resources/es.lproj/Localizable.strings
index 6fbfb23dda..52cf7831f3 100644
--- a/platform/ios/resources/es.lproj/Localizable.strings
+++ b/platform/ios/resources/es.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Muestra más información";
/* No comment provided by engineer. */
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "Cancelar";
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Regresa al mapa";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Gira el mapa para hacer frente al norte";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Acerca de este mapa";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "No se pudo cargar el mapa debido a un error desconocido.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "No se pudo cargar el mapa debido a un error de carga en el estilo.";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -38,10 +47,19 @@
"MAP_A11Y_LABEL" = "Mapa";
/* Map accessibility value */
-"MAP_A11Y_VALUE" = "Zoom %1$dx\nAnotaciones visibles: %2$ld";
+"MAP_A11Y_VALUE" = "Zoom x%1$d\nAnotaciones visibles: %2$ld";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "No se pudo cargar el mapa debido a que el estilo está dañado.";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
+
+/* Developer-only SDK update notification; {latest version, in format x.x.x} */
+"SDK_UPDATE_AVAILABLE" = "La versión %@ de Mapbox Maps SDK for iOS está disponible:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "No se pudo cargar el mapa debido a que no se encuentra el estilo o está incompleto.";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Ayudas a mejorar los mapas de OpenStreetMap y Mapbox al aportar datos de uso anónimos.";
diff --git a/platform/ios/resources/fr.lproj/Localizable.strings b/platform/ios/resources/fr.lproj/Localizable.strings
index 075042c695..7c02663d47 100644
--- a/platform/ios/resources/fr.lproj/Localizable.strings
+++ b/platform/ios/resources/fr.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Voir plus d’informations";
/* No comment provided by engineer. */
@@ -10,6 +10,9 @@
/* 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";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Tourne la carte vers le nord";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "À propos de cette carte";
+/* 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_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style ne peut pas être chargé.";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -40,8 +49,17 @@
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible(s)";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style est corrompu.";
+
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for 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 :";
+
+/* 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.";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Vous pouvez contribuer à OpenStreetMap et Mapbox en partageant des données d’utilisation anonymes.";
diff --git a/platform/ios/resources/hu.lproj/Localizable.strings b/platform/ios/resources/hu.lproj/Localizable.strings
new file mode 100644
index 0000000000..e4c9882600
--- /dev/null
+++ b/platform/ios/resources/hu.lproj/Localizable.strings
@@ -0,0 +1,93 @@
+/* Accessibility hint */
+"ANNOTATION_A11Y_HINT" = "Több infót mutat";
+
+/* No comment provided by engineer. */
+"API_CLIENT_400_DESC" = "The session data task failed. Original request was: %@";
+
+/* No comment provided by engineer. */
+"API_CLIENT_400_REASON" = "A státuszkód %ld volt";
+
+/* No comment provided by engineer. */
+"CANCEL" = "Mégse";
+
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Visszatér a térképhez";
+
+/* Accessibility hint */
+"COMPASS_A11Y_HINT" = "Elforgatja a térképet, hogy észak felé nézzen";
+
+/* Accessibility label */
+"COMPASS_A11Y_LABEL" = "Iránytű";
+
+/* Compass abbreviation for north */
+"COMPASS_NORTH" = "É";
+
+/* Instructions in Interface Builder designable; {key}, {plist file name} */
+"DESIGNABLE" = "To display a Mapbox-hosted map here, set %1$@ to your access token in %2$@\n\nFor detailed instructions, see:";
+
+/* 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" = "Shows credits, a feedback form, and more";
+
+/* Accessibility label */
+"INFO_A11Y_LABEL" = "Erről a térképről";
+
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert ismeretlen hiba történt.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílust nem lehetett betölteni.";
+
+/* Accessibility label */
+"LOGO_A11Y_LABEL" = "Mapbox";
+
+/* Accessibility label */
+"MAP_A11Y_LABEL" = "Térkép";
+
+/* Map accessibility value */
+"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílus sérült.";
+
+/* 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 %@ mostantól elérhető:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Nem sikerült betölteni a térképet, mert a stílus nem található vagy inkompatibilis.";
+
+/* Telemetry prompt message */
+"TELEMETRY_DISABLED_MSG" = "You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
+
+/* Telemetry prompt button */
+"TELEMETRY_DISABLED_OFF" = "Nem veszek részt";
+
+/* Telemetry prompt button */
+"TELEMETRY_DISABLED_ON" = "Részt veszek";
+
+/* Telemetry prompt message */
+"TELEMETRY_ENABLED_MSG" = "You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
+
+/* Telemetry prompt button */
+"TELEMETRY_ENABLED_OFF" = "Részvétel befejezése";
+
+/* Telemetry prompt button */
+"TELEMETRY_ENABLED_ON" = "Részvétel folytatása";
+
+/* Telemetry prompt button */
+"TELEMETRY_MORE" = "Többet akarok tudni";
+
+/* Action in attribution sheet */
+"TELEMETRY_NAME" = "Mapbox telemetria";
+
+/* Telemetry prompt title */
+"TELEMETRY_TITLE" = "Tedd jobbá a Mapbox térképeket";
+
+/* Default user location annotation title */
+"USER_DOT_TITLE" = "Itt vagy";
+
diff --git a/platform/ios/resources/ja.lproj/Localizable.strings b/platform/ios/resources/ja.lproj/Localizable.strings
index b8fde1cdb2..0bcb706cae 100644
--- a/platform/ios/resources/ja.lproj/Localizable.strings
+++ b/platform/ios/resources/ja.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "詳細を伝える";
/* No comment provided by engineer. */
@@ -41,10 +41,10 @@
"MAP_A11Y_VALUE" = "ズーム %1$d倍\n%2$ld ピン現れる";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "現在Mapbox iOS SDK %1$@が入手できる:";
+"SDK_UPDATE_AVAILABLE" = "現在Mapbox Maps SDK for iOS %1$@が入手できる:";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
diff --git a/platform/ios/resources/lt.lproj/Localizable.strings b/platform/ios/resources/lt.lproj/Localizable.strings
index 3ac683fe40..e8424434b9 100644
--- a/platform/ios/resources/lt.lproj/Localizable.strings
+++ b/platform/ios/resources/lt.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Rodo daugiau informacijos";
/* No comment provided by engineer. */
@@ -53,10 +53,10 @@
"PARSE_STYLE_FAILED_DESC" = "Nepavyko užkrauti žemėlapio, nes stilius yra netinkamo formato.";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK versija %@ jau prieinama.";
+"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS versija %@ jau prieinama.";
/* User-friendly error description */
"STYLE_NOT_FOUND_DESC" = "Nepavyko užkrauti žemėlapio, nes neįmanoma rasti stiliaus arba jis nėra suderinamas.";
diff --git a/platform/ios/resources/lt.lproj/Localizable.stringsdict b/platform/ios/resources/lt.lproj/Localizable.stringsdict
index 0200327f04..732a8d23ac 100644
--- a/platform/ios/resources/lt.lproj/Localizable.stringsdict
+++ b/platform/ios/resources/lt.lproj/Localizable.stringsdict
@@ -14,11 +14,11 @@
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
- <string>Priartinimas: %dx</string>
+ <string>Priartinimas %dx</string>
<key>few</key>
- <string>Priartinimas: %dx</string>
+ <string>Priartinimas %dx</string>
<key>other</key>
- <string>Priartinimas: %dx</string>
+ <string>Priartinimas %dx</string>
</dict>
<key>count</key>
<dict>
@@ -27,11 +27,11 @@
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
- <string>Matomos anotacijos: %d anotacija</string>
+ <string>%d matoma anotacija</string>
<key>few</key>
- <string>Matomos anotacijos: %d anotacijos</string>
+ <string>%d matomos anotacijos</string>
<key>other</key>
- <string>Matomos anotacijos: %d anotacijų</string>
+ <string>%d matomų anotacijų</string>
</dict>
</dict>
</dict>
diff --git a/platform/ios/resources/pt-BR.lproj/Localizable.strings b/platform/ios/resources/pt-BR.lproj/Localizable.strings
index 56eaa7cf9f..4e7e998ab3 100644
--- a/platform/ios/resources/pt-BR.lproj/Localizable.strings
+++ b/platform/ios/resources/pt-BR.lproj/Localizable.strings
@@ -1,8 +1,8 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Mostrar mais informações";
/* No comment provided by engineer. */
-"API_CLIENT_400_DESC" = "The session data task failed. Original request was: %@";
+"API_CLIENT_400_DESC" = "Tarefa de dados da sessão falhou. Requisição original: %@";
/* No comment provided by engineer. */
"API_CLIENT_400_REASON" = "O código de status foi %ld";
@@ -10,6 +10,9 @@
/* No comment provided by engineer. */
"CANCEL" = "Cancelar";
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Retornar ao mapa";
+
/* Accessibility hint */
"COMPASS_A11Y_HINT" = "Rotaciona o mapa com face ao norte";
@@ -20,7 +23,7 @@
"COMPASS_NORTH" = "N";
/* Instructions in Interface Builder designable; {key}, {plist file name} */
-"DESIGNABLE" = "To display a Mapbox-hosted map here, set %1$@ to your access token in %2$@\n\nFor detailed instructions, see:";
+"DESIGNABLE" = "Para exibir um mapa hospedado no Mapbox aqui, insira %1$@ para seu token de acesso %2$@\n\nPara maiores detalhes, veja:";
/* Setup documentation URL display string; keep as short as possible */
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Sobre este mapa";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Falha ao carregar mapa devido a um erro desconhecido";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Falha ao carregar mapa porque o estilo não pode ser carregado";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -40,11 +49,20 @@
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld anotações visíveis";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Falha ao carregar mapa porque o estilo está corrompido.";
+
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
+
+/* Developer-only SDK update notification; {latest version, in format x.x.x} */
+"SDK_UPDATE_AVAILABLE" = "SDK Mapbox para iOS versão %@ está disponível:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Falha ao carregar mapa porque o estilo não pode ser encontrado ou é incompatível.";
/* Telemetry prompt message */
-"TELEMETRY_DISABLED_MSG" = "You can help make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
+"TELEMETRY_DISABLED_MSG" = "Você pode ajudar a tornar o OpenStreetMap e Mapbox ainda melhor contribuindo anonimamente com seus dados de uso.";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_OFF" = "Não Participar";
@@ -53,7 +71,7 @@
"TELEMETRY_DISABLED_ON" = "Participar";
/* Telemetry prompt message */
-"TELEMETRY_ENABLED_MSG" = "You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.";
+"TELEMETRY_ENABLED_MSG" = "Você está ajudando a tornar o OpenStreetMap e Mapbox ainda melhor contribuindo anonimamente com seus dados de uso.";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_OFF" = "Parar de Participar";
diff --git a/platform/ios/resources/ru.lproj/Localizable.strings b/platform/ios/resources/ru.lproj/Localizable.strings
index 1c3b46f057..6d5ea9025e 100644
--- a/platform/ios/resources/ru.lproj/Localizable.strings
+++ b/platform/ios/resources/ru.lproj/Localizable.strings
@@ -1,8 +1,8 @@
-/* Accessibility hint */
+/* Accessibility 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" = "The session data task failed. Original request was:%@";
/* No comment provided by engineer. */
"API_CLIENT_400_REASON" = "The status code was %ld";
@@ -10,6 +10,9 @@
/* 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" = "Повернуть карту на север";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Об этой карте";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Не удалось загрузить карту из-за неизвестной ошибки.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту так как невозможно загрузить стиль.";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -40,8 +49,17 @@
/* Map accessibility value */
"MAP_A11Y_VALUE" = "Масштаб %1$dx\n%2$ld аннотации(й) видны";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Не удалось загрузить карту из-за ошибки в стиле.";
+
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"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 версии %@теперь доступен.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Не удалось загрузить карту так как стиль не найден или несовместим.";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "Вы можете помочь сделать карты OpenStreetMap и Mapbox лучше путем предоставления анонимных данных об использовании.";
diff --git a/platform/ios/resources/sv.lproj/Localizable.strings b/platform/ios/resources/sv.lproj/Localizable.strings
index fb787b973a..df4f3554ff 100644
--- a/platform/ios/resources/sv.lproj/Localizable.strings
+++ b/platform/ios/resources/sv.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Visa mer information";
/* No comment provided by engineer. */
@@ -38,7 +38,7 @@
"LOAD_MAP_FAILED_DESC" = "Kartan kunde inte laddas på grund av att ett okänt fel inträffade.";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "The map failed to load because the style can't be loaded.";
+"LOAD_STYLE_FAILED_DESC" = "Kartan kunde inte laddas på grund av att stilen är skadad.";
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -50,16 +50,16 @@
"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotering(ar) synliga";
/* User-friendly error description */
-"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted.";
+"PARSE_STYLE_FAILED_DESC" = "Kartan kunde inte laddas på grund av att stilen är skadad.";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK version %@ är nu tillgängligt:";
+"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS version %@ är nu tillgängligt:";
/* User-friendly error description */
-"STYLE_NOT_FOUND_DESC" = "The map failed to load because the style can’t be found or is incompatible.";
+"STYLE_NOT_FOUND_DESC" = "Kartan kunde inte laddas på grund av ett okänt fel.";
/* 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.";
diff --git a/platform/ios/resources/uk.lproj/Localizable.strings b/platform/ios/resources/uk.lproj/Localizable.strings
index 86873c69c0..79b9779cc6 100644
--- a/platform/ios/resources/uk.lproj/Localizable.strings
+++ b/platform/ios/resources/uk.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Показує більше інформації";
/* No comment provided by engineer. */
@@ -10,6 +10,9 @@
/* 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" = "Обертає напрямок мапи на північ";
@@ -31,6 +34,12 @@
/* Accessibility label */
"INFO_A11Y_LABEL" = "Про мапу";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Неможливо завантажити мапу через невідому помилку.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Неможливо завантажити мапу, бо неможливо завантажити стиль.";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -38,13 +47,22 @@
"MAP_A11Y_LABEL" = "Мапа";
/* Map accessibility value */
-"MAP_A11Y_VALUE" = "Zoom %1$dx\n%2$ld annotation(s) visible";
+"MAP_A11Y_VALUE" = "Масштаб %1$dx\n%2$ld підпис(ів) показано";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Неможливо завантажити мапу, через помилки в стилі.";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"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:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Неможливо завантажити мапу, бо неможливо знайти стиль або він несумісний.";
/* Telemetry prompt message */
-"TELEMETRY_DISABLED_MSG" = "Ви можете допомогти зробити мапи OpenStreetMap та Mapbox кращими надаючі анонімізовані дані про користування застосунком.";
+"TELEMETRY_DISABLED_MSG" = "Ви можете допомогти зробити мапи OpenStreetMap та Mapbox кращими надаючи анонімізовані дані про користування застосунком.";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_OFF" = "Відмовитись";
@@ -53,7 +71,7 @@
"TELEMETRY_DISABLED_ON" = "Брати участь";
/* Telemetry prompt message */
-"TELEMETRY_ENABLED_MSG" = "Ви допомагаєте робити мапи OpenStreetMap та Mapbox кращими надаючі анонімізовані дані про користування застосунком.";
+"TELEMETRY_ENABLED_MSG" = "Ви допомагаєте робити мапи OpenStreetMap та Mapbox кращими надаючи анонімізовані дані про користування застосунком.";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_OFF" = "Припинити участь";
@@ -68,7 +86,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
new file mode 100644
index 0000000000..a81b01477e
--- /dev/null
+++ b/platform/ios/resources/uk.lproj/Localizable.stringsdict
@@ -0,0 +1,38 @@
+<?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</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@level@
+%#@count@</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>Масштаб %dx</string>
+ </dict>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>one</key>
+ <string>Показано %d підпис</string>
+ <key>few</key>
+ <string>Показано %d підписи</string>
+ <key>other</key>
+ <string>Показано %d підпис(ів)</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/ios/resources/vi.lproj/Localizable.strings b/platform/ios/resources/vi.lproj/Localizable.strings
index 8a35d5d5d6..ec76339440 100644
--- a/platform/ios/resources/vi.lproj/Localizable.strings
+++ b/platform/ios/resources/vi.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "Hiển thị thêm thông tin";
/* No comment provided by engineer. */
@@ -53,10 +53,10 @@
"PARSE_STYLE_FAILED_DESC" = "Bản đồ bị thất bại khi tải vì bảng kiểu bị hỏng.";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK mới ra phiên bản %@:";
+"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS mới ra phiên bản %@:";
/* User-friendly error description */
"STYLE_NOT_FOUND_DESC" = "Bản đồ bị thất bại khi tải vì không tìm thấy bảng kiểu hoặc bảng kiểu không tương thích.";
diff --git a/platform/ios/resources/zh-Hans.lproj/Localizable.strings b/platform/ios/resources/zh-Hans.lproj/Localizable.strings
index 5167e93c1b..1a3453e209 100644
--- a/platform/ios/resources/zh-Hans.lproj/Localizable.strings
+++ b/platform/ios/resources/zh-Hans.lproj/Localizable.strings
@@ -1,4 +1,4 @@
-/* Accessibility hint */
+/* Accessibility hint */
"ANNOTATION_A11Y_HINT" = "显示信息";
/* No comment provided by engineer. */
@@ -53,10 +53,10 @@
"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted.";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"SDK_NAME" = "Mapbox Maps SDK for iOS";
/* Developer-only SDK update notification; {latest version, in format x.x.x} */
-"SDK_UPDATE_AVAILABLE" = "Mapbox iOS SDK version %@ is now available:";
+"SDK_UPDATE_AVAILABLE" = "Mapbox Maps SDK for iOS version %@ is now available:";
/* User-friendly error description */
"STYLE_NOT_FOUND_DESC" = "The map failed to load because the style can’t be found or is incompatible.";
diff --git a/platform/ios/resources/zh-Hant.lproj/Localizable.strings b/platform/ios/resources/zh-Hant.lproj/Localizable.strings
index 1b4e7416d9..d660a3d1ae 100644
--- a/platform/ios/resources/zh-Hant.lproj/Localizable.strings
+++ b/platform/ios/resources/zh-Hant.lproj/Localizable.strings
@@ -10,6 +10,9 @@
/* 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" = "旋轉地圖使正北朝上";
@@ -19,24 +22,24 @@
/* Compass abbreviation for north */
"COMPASS_NORTH" = "北";
-/* Copyright notice in attribution sheet */
-"COPY_MAPBOX" = "© Mapbox";
-
-/* Copyright notice in attribution sheet */
-"COPY_OSM" = "© OpenStreetMap";
-
/* Instructions in Interface Builder designable; {key}, {plist file name} */
-"DESIGNABLE" = "在%2$@中將你的access token設爲%1$@可在這裏顯示Mapbox上的地圖\n\n更多說明請見";
+"DESIGNABLE" = "在%2$@中將你的access token設爲%1$@即可顯示Mapbox上的地圖\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" = "關於這個地圖";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "發生不知名的錯誤,無法載入地圖。";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "載入樣式表時發生錯誤,無法載入地圖。";
+
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -44,13 +47,19 @@
"MAP_A11Y_LABEL" = "地圖";
/* Map accessibility value */
-"MAP_A11Y_VALUE" = "地圖縮放%1$d倍\n有%2$ld處標記可見";
+"MAP_A11Y_VALUE" = "縮放地圖%1$d倍\n可顯示%2$ld處標記";
-/* Action in attribution sheet */
-"MAP_FEEDBACK" = "改進地圖";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "樣式表有毀損,無法載入地圖。";
/* Action sheet title */
-"SDK_NAME" = "Mapbox iOS SDK";
+"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 %@版現已開放下載:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "找不到樣式表或樣式表不兼容,無法載入地圖。";
/* Telemetry prompt message */
"TELEMETRY_DISABLED_MSG" = "你可以提供匿名數據來幫助OpenStreetMap和Mapbox的地圖變得更好。";
@@ -62,7 +71,7 @@
"TELEMETRY_DISABLED_ON" = "我要參與";
/* Telemetry prompt message */
-"TELEMETRY_ENABLED_MSG" = "你的匿名數據在幫助OpenStreetMap和Mapbox的地圖變得更好。";
+"TELEMETRY_ENABLED_MSG" = "你的匿名數據正在改善OpenStreetMap和Mapbox地圖。";
/* Telemetry prompt button */
"TELEMETRY_ENABLED_OFF" = "不再參與";
@@ -74,11 +83,11 @@
"TELEMETRY_MORE" = "詳細信息";
/* Action in attribution sheet */
-"TELEMETRY_NAME" = "Mapbox傳感數據";
+"TELEMETRY_NAME" = "Mapbox遙測";
/* Telemetry prompt title */
"TELEMETRY_TITLE" = "讓Mapbox地圖變得更好";
/* Default user location annotation title */
-"USER_DOT_TITLE" = "你在這裏";
+"USER_DOT_TITLE" = "你在這裡";
diff --git a/platform/ios/resources/zh-Hant.lproj/Localizable.stringsdict b/platform/ios/resources/zh-Hant.lproj/Localizable.stringsdict
new file mode 100644
index 0000000000..fc44e2e501
--- /dev/null
+++ b/platform/ios/resources/zh-Hant.lproj/Localizable.stringsdict
@@ -0,0 +1,30 @@
+<?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</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@level@
+%#@count@</string>
+ <key>level</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>other</key>
+ <string>縮放層級%dx</string>
+ </dict>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>other</key>
+ <string>可見%d處標示</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/ios/scripts/deploy-packages.sh b/platform/ios/scripts/deploy-packages.sh
index 4a3c73295a..103e53768c 100755
--- a/platform/ios/scripts/deploy-packages.sh
+++ b/platform/ios/scripts/deploy-packages.sh
@@ -58,17 +58,17 @@ BINARY_DIRECTORY=${BINARY_DIRECTORY:-build/ios/deploy}
GITHUB_RELEASE=${GITHUB_RELEASE:-true}
PUBLISH_PRE_FLAG=''
+if [[ -z `which github-release` ]]; then
+ step "Installing github-release…"
+ brew install github-release
+ if [ -z `which github-release` ]; then
+ echo "Unable to install github-release. See: https://github.com/aktau/github-release"
+ exit 1
+ fi
+fi
+
if [[ ${GITHUB_RELEASE} = "true" ]]; then
GITHUB_RELEASE=true # Assign bool, not just a string
-
- if [[ -z `which github-release` ]]; then
- step "Installing github-release…"
- brew install github-release
- if [ -z `which github-release` ]; then
- echo "Unable to install github-release. See: https://github.com/aktau/github-release"
- exit 1
- fi
- fi
fi
if [[ -z ${VERSION_TAG} ]]; then
@@ -83,7 +83,7 @@ if [[ $( echo ${VERSION_TAG} | grep --invert-match ios-v ) ]]; then
exit 1
fi
-if [[ $( curl --head https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/releases/tags/${VERSION_TAG} | head -n 1 | grep -c "404 Not Found") == 0 ]]; then
+if github-release info --tag ${VERSION_TAG} | grep --quiet "draft: ✗"; then
echo "Error: ${VERSION_TAG} has already been published on GitHub"
echo "See: https://github.com/${GITHUB_USER}/${GITHUB_REPO}/releases/tag/${VERSION_TAG}"
exit 1
@@ -112,6 +112,5 @@ buildPackageStyle "ipackage" "symbols"
buildPackageStyle "ipackage-strip"
buildPackageStyle "iframework" "symbols-dynamic"
buildPackageStyle "iframework SYMBOLS=NO" "dynamic"
-buildPackageStyle "ifabric" "fabric"
step "Finished deploying ${PUBLISH_VERSION} in $(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds"
diff --git a/platform/ios/scripts/document.sh b/platform/ios/scripts/document.sh
index 170debb625..57b596a4b9 100755
--- a/platform/ios/scripts/document.sh
+++ b/platform/ios/scripts/document.sh
@@ -6,7 +6,7 @@ set -u
if [ -z `which jazzy` ]; then
echo "Installing jazzy…"
- gem install jazzy
+ gem install jazzy --no-rdoc --no-ri
if [ -z `which jazzy` ]; then
echo "Unable to install jazzy. See https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/INSTALL.md"
exit 1
diff --git a/platform/ios/scripts/package.sh b/platform/ios/scripts/package.sh
index e4403c4652..3a7342034a 100755
--- a/platform/ios/scripts/package.sh
+++ b/platform/ios/scripts/package.sh
@@ -21,33 +21,19 @@ elif [[ ${FORMAT} == "dynamic" ]]; then
BUILD_STATIC=false
fi
-SELF_CONTAINED=${SELF_CONTAINED:-}
-STATIC_BUNDLE_DIR=
-if [[ ${SELF_CONTAINED} ]]; then
- STATIC_BUNDLE_DIR="${OUTPUT}/static/${NAME}.framework"
-else
- STATIC_BUNDLE_DIR="${OUTPUT}/static"
-fi
-
-STATIC_SETTINGS_DIR=
-if [[ ${SELF_CONTAINED} ]]; then
- STATIC_SETTINGS_DIR="${OUTPUT}/static/${NAME}.framework"
-else
- STATIC_SETTINGS_DIR="${OUTPUT}"
-fi
-
SDK=iphonesimulator
if [[ ${BUILD_FOR_DEVICE} == true ]]; then
SDK=iphoneos
fi
IOS_SDK_VERSION=`xcrun --sdk ${SDK} --show-sdk-version`
-echo "Configuring ${FORMAT:-dynamic and static} ${BUILDTYPE} framework for ${SDK}; symbols: ${SYMBOLS}; self-contained static framework: ${SELF_CONTAINED:-NO}"
-
function step { >&2 echo -e "\033[1m\033[36m* $@\033[0m"; }
function finish { >&2 echo -en "\033[0m"; }
trap finish EXIT
+step "Configuring ${FORMAT:-dynamic and static} ${BUILDTYPE} framework for ${SDK} ${IOS_SDK_VERSION}; symbols: ${SYMBOLS}"
+
+xcodebuild -version
rm -rf ${OUTPUT}
if [[ ${BUILD_STATIC} == true ]]; then
@@ -69,7 +55,7 @@ PROJ_VERSION=$(git rev-list --count HEAD)
SEM_VERSION=$( git describe --tags --match=ios-v*.*.* --abbrev=0 | sed 's/^ios-v//' )
SHORT_VERSION=${SEM_VERSION%-*}
-step "Building targets (build ${PROJ_VERSION}, version ${SEM_VERSION})…"
+step "Building targets (build ${PROJ_VERSION}, version ${SEM_VERSION})"
SCHEME='dynamic'
if [[ ${BUILD_DYNAMIC} == true && ${BUILD_STATIC} == true ]]; then
@@ -78,6 +64,7 @@ elif [[ ${BUILD_STATIC} == true ]]; then
SCHEME='static'
fi
+step "Building for iOS Simulator using scheme ${SCHEME}"
xcodebuild \
CURRENT_PROJECT_VERSION=${PROJ_VERSION} \
CURRENT_SHORT_VERSION=${SHORT_VERSION} \
@@ -92,6 +79,7 @@ xcodebuild \
-jobs ${JOBS} | xcpretty
if [[ ${BUILD_FOR_DEVICE} == true ]]; then
+ step "Building for iOS devices using scheme ${SCHEME}"
xcodebuild \
CURRENT_PROJECT_VERSION=${PROJ_VERSION} \
CURRENT_SHORT_VERSION=${SHORT_VERSION} \
@@ -119,7 +107,7 @@ if [[ ${BUILD_FOR_DEVICE} == true ]]; then
${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphonesimulator/lib} \
`cmake -LA -N ${DERIVED_DATA} | grep MASON_PACKAGE_icu_LIBRARIES | cut -d= -f2`
- cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.bundle ${STATIC_BUNDLE_DIR}
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/${NAME}.bundle ${OUTPUT}/static
fi
if [[ ${BUILD_DYNAMIC} == true ]]; then
@@ -149,7 +137,7 @@ if [[ ${BUILD_FOR_DEVICE} == true ]]; then
-create -output ${OUTPUT}/dynamic/${NAME}.framework/${NAME} | echo
fi
- cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/Settings.bundle ${STATIC_SETTINGS_DIR}
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphoneos/Settings.bundle ${OUTPUT}
else
if [[ ${BUILD_STATIC} == true ]]; then
step "Assembling static library for iOS Simulator…"
@@ -159,7 +147,7 @@ else
${LIBS[@]/#/${PRODUCTS}/${BUILDTYPE}-iphonesimulator/lib} \
`cmake -LA -N ${DERIVED_DATA} | grep MASON_PACKAGE_icu_LIBRARIES | cut -d= -f2`
- cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.bundle ${STATIC_BUNDLE_DIR}
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/${NAME}.bundle ${OUTPUT}/static
fi
if [[ ${BUILD_DYNAMIC} == true ]]; then
@@ -174,7 +162,7 @@ else
fi
fi
- cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/Settings.bundle ${STATIC_SETTINGS_DIR}
+ cp -rv ${PRODUCTS}/${BUILDTYPE}-iphonesimulator/Settings.bundle ${OUTPUT}
fi
if [[ ${SYMBOLS} = NO ]]; then
@@ -244,13 +232,17 @@ if [[ ${BUILD_STATIC} == true ]]; then
fi
step "Copying library resources…"
-cp -pv LICENSE.md ${STATIC_SETTINGS_DIR}
+cp -pv LICENSE.md ${OUTPUT}
if [[ ${BUILD_STATIC} == true ]]; then
- cp -pv "${STATIC_BUNDLE_DIR}/${NAME}.bundle/Info.plist" "${OUTPUT}/static/${NAME}.framework/Info.plist"
+ cp -pv "${OUTPUT}/static/${NAME}.bundle/Info.plist" "${OUTPUT}/static/${NAME}.framework/Info.plist"
plutil -replace CFBundlePackageType -string FMWK "${OUTPUT}/static/${NAME}.framework/Info.plist"
mkdir "${OUTPUT}/static/${NAME}.framework/Modules"
cp -pv platform/ios/framework/modulemap "${OUTPUT}/static/${NAME}.framework/Modules/module.modulemap"
fi
+if [[ ${BUILD_DYNAMIC} == true && ${BUILD_FOR_DEVICE} == true ]]; then
+ step "Copying bitcode symbol maps…"
+ find "${PRODUCTS}/${BUILDTYPE}-iphoneos" -name '*.bcsymbolmap' -type f -exec cp -pv {} "${OUTPUT}/dynamic/" \;
+fi
sed -n -e '/^## /,$p' platform/ios/CHANGELOG.md > "${OUTPUT}/CHANGELOG.md"
rm -rf /tmp/mbgl
diff --git a/platform/ios/scripts/release-fabric.sh b/platform/ios/scripts/release-fabric.sh
deleted file mode 100755
index a523705b7b..0000000000
--- a/platform/ios/scripts/release-fabric.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -o pipefail
-set -u
-
-export PUBLISH_VERSION=$1
-export BINARY_DIRECTORY=$2
-export ZIP_OUTPUT=mapbox-ios-sdk-${PUBLISH_VERSION}-fabric
-export FILE_NAME=mapbox-ios-sdk-${PUBLISH_VERSION}-fabric.zip
-export ZIP_ARCHIVE_PATH=${BINARY_DIRECTORY}/${FILE_NAME}
-export BUNDLE_ID="com.mapbox.sdk.ios"
-
-echo "Downloading ${FILE_NAME}:"
-wget -P ${BINARY_DIRECTORY} http://mapbox.s3.amazonaws.com/mapbox-gl-native/ios/builds/${FILE_NAME}
-
-echo "Extracting ${ZIP_ARCHIVE_PATH} to ${BINARY_DIRECTORY}/${ZIP_OUTPUT}"
-unzip -q ${ZIP_ARCHIVE_PATH} -d ${BINARY_DIRECTORY}/${ZIP_OUTPUT}
-ditto ${BINARY_DIRECTORY}/${ZIP_OUTPUT}/static/Mapbox.framework ${BINARY_DIRECTORY}/Mapbox.framework
-
-echo "Zipping framework:"
-cd ${BINARY_DIRECTORY}
-zip -q -r Mapbox.framework.zip Mapbox.framework
-cd $OLDPWD
-
-echo "Validating framework:"
-./validate-fabric-zip.sh ${BINARY_DIRECTORY}/Mapbox.framework.zip
-
-echo "Uploading ${BINARY_DIRECTORY}/Mapbox.framework.zip to https://kits.fabric.io/manage-api/v1/kit-releases/ios/$BUNDLE_ID/$PUBLISH_VERSION with key ${FABRIC_KIT_API_KEY}"
-curl --fail -v -X PUT -H "X-FabricKits-ApiKey: ${FABRIC_KIT_API_KEY}" \
- -F "release_artifact=@${BINARY_DIRECTORY}/Mapbox.framework.zip;type=application/octet-stream" \
- https://kits.fabric.io/manage-api/v1/kit-releases/ios/$BUNDLE_ID/$PUBLISH_VERSION
-
-echo "Cleaning up"
-rm -r #{BINARY_DIRECTORY}
-
-echo "Done"
diff --git a/platform/ios/scripts/validate-fabric-zip.sh b/platform/ios/scripts/validate-framework-zip.sh
index 2cd1e90ee7..2cd1e90ee7 100755
--- a/platform/ios/scripts/validate-fabric-zip.sh
+++ b/platform/ios/scripts/validate-framework-zip.sh
diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m
index 124d436197..8a987d76d8 100644
--- a/platform/ios/src/MGLAPIClient.m
+++ b/platform/ios/src/MGLAPIClient.m
@@ -17,8 +17,10 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
@property (nonatomic, copy) NSURLSession *session;
@property (nonatomic, copy) NSURL *baseURL;
-@property (nonatomic, copy) NSData *digicertCert;
-@property (nonatomic, copy) NSData *geoTrustCert;
+@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 *testServerCert;
@property (nonatomic, copy) NSString *userAgent;
@property (nonatomic) BOOL usesTestServer;
@@ -107,10 +109,14 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
- (void)loadCertificates {
NSData *certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust"];
- self.geoTrustCert = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert"];
- self.digicertCert = 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_staging"];
self.testServerCert = certificate;
}
@@ -141,75 +147,53 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
#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* give use revocation checking
+
+ // Validate the certificate chain with the device's trust store anyway this *might* use revocation checking
SecTrustEvaluate(serverTrust, &trustResult);
- if (trustResult == kSecTrustResultUnspecified)
- {
+
+ 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
- long numKeys = SecTrustGetCertificateCount(serverTrust);
-
- BOOL found = NO;
- // Try GeoTrust Cert First
- for (int lc = 0; lc < numKeys; lc++) {
- SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
- NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
-
- // Compare Remote Key With Local Version
- if ([remoteCertificateData isEqualToData:_geoTrustCert]) {
- // Found the certificate; continue connecting
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- found = YES;
- break;
- }
+ CFIndex numKeys = SecTrustGetCertificateCount(serverTrust);
+
+ // Check certs in the following order: digicert 2016, digicert 2017, geotrust 2016, geotrust 2017
+ 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) {
- // Fallback to Digicert Cert
- for (int lc = 0; lc < numKeys; lc++) {
- SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
- NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
-
- // Compare Remote Key With Local Version
- if ([remoteCertificateData isEqualToData:_digicertCert]) {
- // Found the certificate; continue connecting
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- found = YES;
- break;
- }
- }
-
- if (!found && _usesTestServer) {
- // See if this is test server
- for (int lc = 0; lc < numKeys; lc++) {
- SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
- NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
-
- // Compare Remote Key With Local Version
- if ([remoteCertificateData isEqualToData:_testServerCert]) {
- // Found the certificate; continue connecting
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- found = YES;
- break;
- }
- }
- }
-
- if (!found) {
- // The certificate wasn't found in GeoTrust nor Digicert. Cancel the connection.
- completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- }
+ 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 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];
}
}
- else
- {
- // Certificate chain validation failed; cancel the connection
+
+ if (!found) {
+ // No certificate was found so cancel the connection.
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}
}
diff --git a/platform/ios/src/MGLAnnotationImage.h b/platform/ios/src/MGLAnnotationImage.h
index fbeee18624..0b5a111841 100644
--- a/platform/ios/src/MGLAnnotationImage.h
+++ b/platform/ios/src/MGLAnnotationImage.h
@@ -1,5 +1,7 @@
#import <UIKit/UIKit.h>
+#import "MGLFoundation.h"
+
NS_ASSUME_NONNULL_BEGIN
/**
@@ -8,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
objects and may be recycled later and put into a reuse queue that is maintained
by the map view.
*/
+MGL_EXPORT
@interface MGLAnnotationImage : NSObject <NSSecureCoding>
#pragma mark Initializing and Preparing the Image Object
diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h
index 2802d31b05..4fa0f196ab 100644
--- a/platform/ios/src/MGLAnnotationView.h
+++ b/platform/ios/src/MGLAnnotationView.h
@@ -1,5 +1,7 @@
#import <UIKit/UIKit.h>
+#import "MGLFoundation.h"
+
NS_ASSUME_NONNULL_BEGIN
@protocol MGLAnnotation;
@@ -50,6 +52,7 @@ typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) {
interactivity such as dragging, you can use an `MGLAnnotationImage` instead to
conserve memory and optimize drawing performance.
*/
+MGL_EXPORT
@interface MGLAnnotationView : UIView <NSSecureCoding>
#pragma mark Initializing and Preparing the View
diff --git a/platform/ios/src/MGLCompactCalloutView.h b/platform/ios/src/MGLCompactCalloutView.h
index 56c48a99e5..5cecf37ff6 100644
--- a/platform/ios/src/MGLCompactCalloutView.h
+++ b/platform/ios/src/MGLCompactCalloutView.h
@@ -7,7 +7,7 @@
callout view displays the represented annotation’s title, subtitle, and
accessory views in a compact, two-line layout.
*/
-@interface MGLCompactCalloutView : SMCalloutView <MGLCalloutView>
+@interface MGLCompactCalloutView : MGLSMCalloutView <MGLCalloutView>
+ (instancetype)platformCalloutView;
diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h
index c48dd6b27b..35fb31a342 100644
--- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h
+++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.h
@@ -1,7 +1,15 @@
#import <UIKit/UIKit.h>
#import "MGLUserLocationAnnotationView.h"
+extern const CGFloat MGLUserLocationAnnotationDotSize;
+extern const CGFloat MGLUserLocationAnnotationHaloSize;
+
+extern const CGFloat MGLUserLocationAnnotationPuckSize;
+extern const CGFloat MGLUserLocationAnnotationArrowSize;
+
+// Threshold in radians between heading indicator rotation updates.
+extern const CGFloat MGLUserLocationHeadingUpdateThreshold;
+
@interface MGLFaux3DUserLocationAnnotationView : MGLUserLocationAnnotationView
@end
-
diff --git a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m
index 6db9c0db10..1ed3d86ad1 100644
--- a/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m
+++ b/platform/ios/src/MGLFaux3DUserLocationAnnotationView.m
@@ -2,14 +2,17 @@
#import "MGLMapView.h"
#import "MGLUserLocation.h"
+#import "MGLUserLocationHeadingIndicator.h"
+#import "MGLUserLocationHeadingArrowLayer.h"
+#import "MGLUserLocationHeadingBeamLayer.h"
const CGFloat MGLUserLocationAnnotationDotSize = 22.0;
const CGFloat MGLUserLocationAnnotationHaloSize = 115.0;
const CGFloat MGLUserLocationAnnotationPuckSize = 45.0;
-const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.6;
+const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuckSize * 0.5;
-#pragma mark -
+const CGFloat MGLUserLocationHeadingUpdateThreshold = 0.01;
@implementation MGLFaux3DUserLocationAnnotationView
{
@@ -18,14 +21,13 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
CALayer *_puckDot;
CAShapeLayer *_puckArrow;
- CALayer *_headingIndicatorLayer;
- CAShapeLayer *_headingIndicatorMaskLayer;
+ CALayer<MGLUserLocationHeadingIndicator> *_headingIndicatorLayer;
CALayer *_accuracyRingLayer;
CALayer *_dotBorderLayer;
CALayer *_dotLayer;
CALayer *_haloLayer;
- double _oldHeadingAccuracy;
+ CLLocationDirection _oldHeadingAccuracy;
CLLocationAccuracy _oldHorizontalAccuracy;
double _oldZoom;
double _oldPitch;
@@ -56,21 +58,18 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
- (void)setTintColor:(UIColor *)tintColor
{
+ CGColorRef newTintColor = [tintColor CGColor];
+
if (_puckModeActivated)
{
- _puckArrow.fillColor = [tintColor CGColor];
+ _puckArrow.fillColor = newTintColor;
}
else
{
- if (_accuracyRingLayer)
- {
- _accuracyRingLayer.backgroundColor = [tintColor CGColor];
- }
-
- _haloLayer.backgroundColor = [tintColor CGColor];
- _dotLayer.backgroundColor = [tintColor CGColor];
-
- _headingIndicatorLayer.contents = (__bridge id)[[self headingIndicatorTintedGradientImage] CGImage];
+ _accuracyRingLayer.backgroundColor = newTintColor;
+ _haloLayer.backgroundColor = newTintColor;
+ _dotLayer.backgroundColor = newTintColor;
+ [_headingIndicatorLayer updateTintColor:newTintColor];
}
}
@@ -80,7 +79,7 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
{
// disable implicit animation
[CATransaction begin];
- [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
+ [CATransaction setDisableActions:YES];
CATransform3D t = CATransform3DRotate(CATransform3DIdentity, MGLRadiansFromDegrees(self.mapView.camera.pitch), 1.0, 0, 0);
self.layer.sublayerTransform = t;
@@ -138,7 +137,6 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
self.layer.sublayers = nil;
_headingIndicatorLayer = nil;
- _headingIndicatorMaskLayer = nil;
_accuracyRingLayer = nil;
_haloLayer = nil;
_dotBorderLayer = nil;
@@ -177,12 +175,16 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
_puckArrow = [CAShapeLayer layer];
_puckArrow.path = [[self puckArrow] CGPath];
_puckArrow.fillColor = [self.mapView.tintColor CGColor];
- _puckArrow.bounds = CGRectMake(0, 0, MGLUserLocationAnnotationArrowSize, MGLUserLocationAnnotationArrowSize);
- _puckArrow.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0);
+ _puckArrow.bounds = CGRectMake(0, 0, round(MGLUserLocationAnnotationArrowSize), round(MGLUserLocationAnnotationArrowSize));
+ _puckArrow.position = CGPointMake(CGRectGetMidX(super.bounds), CGRectGetMidY(super.bounds));
_puckArrow.shouldRasterize = YES;
_puckArrow.rasterizationScale = [UIScreen mainScreen].scale;
_puckArrow.drawsAsynchronously = YES;
+ _puckArrow.lineJoin = @"round";
+ _puckArrow.lineWidth = 1.f;
+ _puckArrow.strokeColor = _puckArrow.fillColor;
+
[self.layer addSublayer:_puckArrow];
}
if (self.userLocation.location.course >= 0)
@@ -225,70 +227,67 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
[self updateFrameWithSize:MGLUserLocationAnnotationDotSize];
}
- BOOL showHeadingIndicator = self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading;
-
- // update heading indicator
+ // heading indicator (tinted, beam or arrow)
//
+ BOOL headingTrackingModeEnabled = self.mapView.userTrackingMode == MGLUserTrackingModeFollowWithHeading;
+ BOOL showHeadingIndicator = self.mapView.showsUserHeadingIndicator || headingTrackingModeEnabled;
+
if (showHeadingIndicator)
{
_headingIndicatorLayer.hidden = NO;
+ CLLocationDirection headingAccuracy = self.userLocation.heading.headingAccuracy;
- // heading indicator (tinted, semi-circle)
- //
- if ( ! _headingIndicatorLayer && self.userLocation.heading.headingAccuracy)
+ if (([_headingIndicatorLayer isMemberOfClass:[MGLUserLocationHeadingBeamLayer class]] && ! headingTrackingModeEnabled) ||
+ ([_headingIndicatorLayer isMemberOfClass:[MGLUserLocationHeadingArrowLayer class]] && headingTrackingModeEnabled))
{
- CGFloat headingIndicatorSize = MGLUserLocationAnnotationHaloSize;
-
- _headingIndicatorLayer = [CALayer layer];
- _headingIndicatorLayer.bounds = CGRectMake(0, 0, headingIndicatorSize, headingIndicatorSize);
- _headingIndicatorLayer.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0);
- _headingIndicatorLayer.contents = (__bridge id)[[self headingIndicatorTintedGradientImage] CGImage];
- _headingIndicatorLayer.contentsGravity = kCAGravityBottom;
- _headingIndicatorLayer.contentsScale = [UIScreen mainScreen].scale;
- _headingIndicatorLayer.opacity = 0.4;
- _headingIndicatorLayer.shouldRasterize = YES;
- _headingIndicatorLayer.rasterizationScale = [UIScreen mainScreen].scale;
- _headingIndicatorLayer.drawsAsynchronously = YES;
-
- [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer];
+ [_headingIndicatorLayer removeFromSuperlayer];
+ _headingIndicatorLayer = nil;
+ _oldHeadingAccuracy = -1;
}
- // heading indicator accuracy mask (fan-shaped)
- //
- if ( ! _headingIndicatorMaskLayer && self.userLocation.heading.headingAccuracy)
+ if ( ! _headingIndicatorLayer && headingAccuracy)
{
- _headingIndicatorMaskLayer = [CAShapeLayer layer];
- _headingIndicatorMaskLayer.frame = _headingIndicatorLayer.bounds;
- _headingIndicatorMaskLayer.path = [[self headingIndicatorClippingMask] CGPath];
-
- // apply the mask to the halo-radius-sized gradient layer
- _headingIndicatorLayer.mask = _headingIndicatorMaskLayer;
-
- _oldHeadingAccuracy = self.userLocation.heading.headingAccuracy;
-
+ if (headingTrackingModeEnabled)
+ {
+ _headingIndicatorLayer = [[MGLUserLocationHeadingBeamLayer alloc] initWithUserLocationAnnotationView:self];
+ [self.layer insertSublayer:_headingIndicatorLayer below:_dotBorderLayer];
+ }
+ else
+ {
+ _headingIndicatorLayer = [[MGLUserLocationHeadingArrowLayer alloc] initWithUserLocationAnnotationView:self];
+ [self.layer addSublayer:_headingIndicatorLayer];
+ _headingIndicatorLayer.zPosition = 1;
+ }
}
- else if (_oldHeadingAccuracy != self.userLocation.heading.headingAccuracy)
- {
- // recalculate the clipping mask based on updated accuracy
- _headingIndicatorMaskLayer.path = [[self headingIndicatorClippingMask] CGPath];
- _oldHeadingAccuracy = self.userLocation.heading.headingAccuracy;
+ if (_oldHeadingAccuracy != headingAccuracy)
+ {
+ [_headingIndicatorLayer updateHeadingAccuracy:headingAccuracy];
+ _oldHeadingAccuracy = headingAccuracy;
}
if (self.userLocation.heading.trueHeading >= 0)
{
- _headingIndicatorLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, -MGLRadiansFromDegrees(self.mapView.direction - self.userLocation.heading.trueHeading));
+ CGFloat rotation = -MGLRadiansFromDegrees(self.mapView.direction - self.userLocation.heading.trueHeading);
+
+ // Don't rotate if the change is imperceptible.
+ if (fabs(rotation) > MGLUserLocationHeadingUpdateThreshold)
+ {
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+
+ _headingIndicatorLayer.affineTransform = CGAffineTransformRotate(CGAffineTransformIdentity, rotation);
+
+ [CATransaction commit];
+ }
}
}
else
{
[_headingIndicatorLayer removeFromSuperlayer];
- [_headingIndicatorMaskLayer removeFromSuperlayer];
_headingIndicatorLayer = nil;
- _headingIndicatorMaskLayer = nil;
}
-
// update accuracy ring (if zoom or horizontal accuracy have changed)
//
if (_accuracyRingLayer && (_oldZoom != self.mapView.zoomLevel || _oldHorizontalAccuracy != self.userLocation.location.horizontalAccuracy))
@@ -301,13 +300,13 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
_accuracyRingLayer.hidden = NO;
// disable implicit animation of the accuracy ring, unless triggered by a change in accuracy
- id shouldDisableActions = (_oldHorizontalAccuracy == self.userLocation.location.horizontalAccuracy) ? (id)kCFBooleanTrue : (id)kCFBooleanFalse;
+ BOOL shouldDisableActions = _oldHorizontalAccuracy == self.userLocation.location.horizontalAccuracy;
[CATransaction begin];
- [CATransaction setValue:shouldDisableActions forKey:kCATransactionDisableActions];
+ [CATransaction setDisableActions:shouldDisableActions];
_accuracyRingLayer.bounds = CGRectMake(0, 0, accuracyRingSize, accuracyRingSize);
- _accuracyRingLayer.cornerRadius = accuracyRingSize / 2;
+ _accuracyRingLayer.cornerRadius = accuracyRingSize / 2.0;
// match the halo to the accuracy ring
_haloLayer.bounds = _accuracyRingLayer.bounds;
@@ -436,9 +435,11 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
- (CALayer *)circleLayerWithSize:(CGFloat)layerSize
{
+ layerSize = round(layerSize);
+
CALayer *circleLayer = [CALayer layer];
circleLayer.bounds = CGRectMake(0, 0, layerSize, layerSize);
- circleLayer.position = CGPointMake(super.bounds.size.width / 2.0, super.bounds.size.height / 2.0);
+ circleLayer.position = CGPointMake(CGRectGetMidX(super.bounds), CGRectGetMidY(super.bounds));
circleLayer.cornerRadius = layerSize / 2.0;
circleLayer.shouldRasterize = YES;
circleLayer.rasterizationScale = [UIScreen mainScreen].scale;
@@ -460,72 +461,8 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
- (CGFloat)calculateAccuracyRingSize
{
- CGFloat latitudeRadians = MGLRadiansFromDegrees(self.userLocation.coordinate.latitude);
- CGFloat metersPerPoint = [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude];
- CGFloat pixelRadius = self.userLocation.location.horizontalAccuracy / cos(latitudeRadians) / metersPerPoint;
-
- return pixelRadius * 2.0;
-}
-
-- (UIImage *)headingIndicatorTintedGradientImage
-{
- UIImage *image;
-
- CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0;
-
- UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0);
-
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGContextRef context = UIGraphicsGetCurrentContext();
-
- // gradient from the tint color to no-alpha tint color
- CGFloat gradientLocations[] = {0.0, 1.0};
- CGGradientRef gradient = CGGradientCreateWithColors(
- colorSpace,
- (__bridge CFArrayRef)@[
- (id)[self.mapView.tintColor CGColor],
- (id)[[self.mapView.tintColor colorWithAlphaComponent:0] CGColor]],
- gradientLocations);
-
- // draw the gradient from the center point to the edge (full halo radius)
- CGPoint centerPoint = CGPointMake(haloRadius, haloRadius);
- CGContextDrawRadialGradient(context, gradient,
- centerPoint, 0.0,
- centerPoint, haloRadius,
- kNilOptions);
-
- image = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
-
- CGGradientRelease(gradient);
- CGColorSpaceRelease(colorSpace);
-
- return image;
-}
-
-- (UIBezierPath *)headingIndicatorClippingMask
-{
- CGFloat accuracy = self.userLocation.heading.headingAccuracy;
-
- // size the mask using accuracy, but keep within a good display range
- CGFloat clippingDegrees = 90 - accuracy;
- clippingDegrees = fmin(clippingDegrees, 70); // most accurate
- clippingDegrees = fmax(clippingDegrees, 10); // least accurate
-
- CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize);
- UIBezierPath *ovalPath = UIBezierPath.bezierPath;
-
- // clip the oval to ± incoming accuracy degrees (converted to radians), from the top
- [ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))
- radius:CGRectGetWidth(ovalRect) / 2.0
- startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees)
- endAngle:MGLRadiansFromDegrees(-clippingDegrees)
- clockwise:YES];
-
- [ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))];
- [ovalPath closePath];
-
- return ovalPath;
+ // diameter in screen points
+ return round(self.userLocation.location.horizontalAccuracy / [self.mapView metersPerPointAtLatitude:self.userLocation.coordinate.latitude] * 2.0);
}
@end
diff --git a/platform/ios/src/MGLMapAccessibilityElement.h b/platform/ios/src/MGLMapAccessibilityElement.h
new file mode 100644
index 0000000000..952f6cbf2f
--- /dev/null
+++ b/platform/ios/src/MGLMapAccessibilityElement.h
@@ -0,0 +1,54 @@
+#import <UIKit/UIKit.h>
+
+#import "MGLFoundation.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol MGLFeature;
+
+/// Unique identifier representing a single annotation in mbgl.
+typedef uint32_t MGLAnnotationTag;
+
+/** An accessibility element representing something that appears on the map. */
+MGL_EXPORT
+@interface MGLMapAccessibilityElement : UIAccessibilityElement
+
+@end
+
+/** An accessibility element representing a map annotation. */
+@interface MGLAnnotationAccessibilityElement : MGLMapAccessibilityElement
+
+/** The tag of the annotation represented by this element. */
+@property (nonatomic) MGLAnnotationTag tag;
+
+- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)identifier NS_DESIGNATED_INITIALIZER;
+
+@end
+
+/** An accessibility element representing a map feature. */
+MGL_EXPORT
+@interface MGLFeatureAccessibilityElement : MGLMapAccessibilityElement
+
+/** The feature represented by this element. */
+@property (nonatomic, strong) id <MGLFeature> feature;
+
+- (instancetype)initWithAccessibilityContainer:(id)container feature:(id <MGLFeature>)feature NS_DESIGNATED_INITIALIZER;
+
+@end
+
+/** An accessibility element representing a place feature. */
+MGL_EXPORT
+@interface MGLPlaceFeatureAccessibilityElement : MGLFeatureAccessibilityElement
+@end
+
+/** An accessibility element representing a road feature. */
+MGL_EXPORT
+@interface MGLRoadFeatureAccessibilityElement : MGLFeatureAccessibilityElement
+@end
+
+/** An accessibility element representing the MGLMapView at large. */
+MGL_EXPORT
+@interface MGLMapViewProxyAccessibilityElement : UIAccessibilityElement
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLMapAccessibilityElement.mm b/platform/ios/src/MGLMapAccessibilityElement.mm
new file mode 100644
index 0000000000..1a2953b0bb
--- /dev/null
+++ b/platform/ios/src/MGLMapAccessibilityElement.mm
@@ -0,0 +1,200 @@
+#import "MGLMapAccessibilityElement.h"
+#import "MGLDistanceFormatter.h"
+#import "MGLCompassDirectionFormatter.h"
+#import "MGLFeature.h"
+
+#import "MGLGeometry_Private.h"
+#import "MGLVectorSource_Private.h"
+
+#import "NSBundle+MGLAdditions.h"
+
+@implementation MGLMapAccessibilityElement
+
+- (UIAccessibilityTraits)accessibilityTraits {
+ return super.accessibilityTraits | UIAccessibilityTraitAdjustable;
+}
+
+- (void)accessibilityIncrement {
+ [self.accessibilityContainer accessibilityIncrement];
+}
+
+- (void)accessibilityDecrement {
+ [self.accessibilityContainer accessibilityDecrement];
+}
+
+@end
+
+@implementation MGLAnnotationAccessibilityElement
+
+- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)tag {
+ if (self = [super initWithAccessibilityContainer:container]) {
+ _tag = tag;
+ self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint");
+ }
+ return self;
+}
+
+- (UIAccessibilityTraits)accessibilityTraits {
+ return super.accessibilityTraits | UIAccessibilityTraitButton;
+}
+
+@end
+
+@implementation MGLFeatureAccessibilityElement
+
+- (instancetype)initWithAccessibilityContainer:(id)container feature:(id<MGLFeature>)feature {
+ if (self = [super initWithAccessibilityContainer:container]) {
+ _feature = feature;
+
+ NSString *languageCode = [MGLVectorSource preferredMapboxStreetsLanguage];
+ NSString *nameAttribute = [NSString stringWithFormat:@"name_%@", languageCode];
+ NSString *name = [feature attributeForKey:nameAttribute];
+
+ // If a feature hasn’t been translated into the preferred language, it
+ // may be in the local language, which may be written in another script.
+ // Romanize it.
+ NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageCode];
+ NSOrthography *orthography;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) {
+ orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier];
+ }
+#pragma clang diagnostic pop
+#endif
+ if ([orthography.dominantScript isEqualToString:@"Latn"]) {
+ name = [name stringByApplyingTransform:NSStringTransformToLatin reverse:NO];
+ }
+
+ self.accessibilityLabel = name;
+ }
+ return self;
+}
+
+- (UIAccessibilityTraits)accessibilityTraits {
+ return super.accessibilityTraits | UIAccessibilityTraitStaticText;
+}
+
+@end
+
+@implementation MGLPlaceFeatureAccessibilityElement
+
+- (instancetype)initWithAccessibilityContainer:(id)container feature:(id<MGLFeature>)feature {
+ if (self = [super initWithAccessibilityContainer:container feature:feature]) {
+ NSDictionary *attributes = feature.attributes;
+ NSMutableArray *facts = [NSMutableArray array];
+
+ // Announce the kind of place or POI.
+ if (attributes[@"type"]) {
+ // FIXME: Unfortunately, these types aren’t a closed set that can be
+ // localized, since they’re based on OpenStreetMap tags.
+ NSString *type = [attributes[@"type"] stringByReplacingOccurrencesOfString:@"_"
+ withString:@" "];
+ [facts addObject:type];
+ }
+ // Announce the kind of airport, rail station, or mountain based on its
+ // Maki image name.
+ else if (attributes[@"maki"]) {
+ // TODO: Localize Maki image names.
+ [facts addObject:attributes[@"maki"]];
+ }
+
+ // Announce the peak’s elevation in the preferred units.
+ if (attributes[@"elevation_m"] ?: attributes[@"elevation_ft"]) {
+ NSLengthFormatter *formatter = [[NSLengthFormatter alloc] init];
+ formatter.unitStyle = NSFormattingUnitStyleLong;
+
+ NSNumber *elevationValue;
+ NSLengthFormatterUnit unit;
+ BOOL usesMetricSystem = ![[formatter.numberFormatter.locale objectForKey:NSLocaleMeasurementSystem]
+ isEqualToString:@"U.S."];
+ if (usesMetricSystem) {
+ elevationValue = attributes[@"elevation_m"];
+ unit = NSLengthFormatterUnitMeter;
+ } else {
+ elevationValue = attributes[@"elevation_ft"];
+ unit = NSLengthFormatterUnitFoot;
+ }
+ [facts addObject:[formatter stringFromValue:elevationValue.doubleValue unit:unit]];
+ }
+
+ if (facts.count) {
+ NSString *separator = NSLocalizedStringWithDefaultValue(@"LIST_SEPARATOR", nil, nil, @", ", @"List separator");
+ self.accessibilityValue = [facts componentsJoinedByString:separator];
+ }
+ }
+ return self;
+}
+
+@end
+
+@implementation MGLRoadFeatureAccessibilityElement
+
+- (instancetype)initWithAccessibilityContainer:(id)container feature:(id<MGLFeature>)feature {
+ if (self = [super initWithAccessibilityContainer:container feature:feature]) {
+ NSDictionary *attributes = feature.attributes;
+ NSMutableArray *facts = [NSMutableArray array];
+
+ // Announce the route number.
+ if (attributes[@"ref"]) {
+ // TODO: Decorate the route number with the network name based on the shield attribute.
+ NSString *ref = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"ROAD_REF_A11Y_FMT", nil, nil, @"Route %@", @"String format for accessibility value for road feature; {route number}"), attributes[@"ref"]];
+ [facts addObject:ref];
+ }
+
+ // Announce whether the road is a one-way road.
+ if ([attributes[@"oneway"] isEqualToString:@"true"]) {
+ [facts addObject:NSLocalizedStringWithDefaultValue(@"ROAD_ONEWAY_A11Y_VALUE", nil, nil, @"One way", @"Accessibility value indicating that a road is a one-way road")];
+ }
+
+ // Announce whether the road is a divided road.
+ MGLPolyline *polyline;
+ if ([feature isKindOfClass:[MGLMultiPolylineFeature class]]) {
+ [facts addObject:NSLocalizedStringWithDefaultValue(@"ROAD_DIVIDED_A11Y_VALUE", nil, nil, @"Divided road", @"Accessibility value indicating that a road is a divided road (dual carriageway)")];
+ polyline = [(MGLMultiPolylineFeature *)feature polylines].firstObject;
+ }
+
+ // Announce the road’s general direction.
+ if ([feature isKindOfClass:[MGLPolylineFeature class]]) {
+ polyline = (MGLPolylineFeature *)feature;
+ }
+ if (polyline) {
+ NSUInteger pointCount = polyline.pointCount;
+ if (pointCount) {
+ CLLocationCoordinate2D *coordinates = polyline.coordinates;
+ CLLocationDirection startDirection = MGLDirectionBetweenCoordinates(coordinates[pointCount - 1], coordinates[0]);
+ CLLocationDirection endDirection = MGLDirectionBetweenCoordinates(coordinates[0], coordinates[pointCount - 1]);
+
+ MGLCompassDirectionFormatter *formatter = [[MGLCompassDirectionFormatter alloc] init];
+ formatter.unitStyle = NSFormattingUnitStyleLong;
+
+ NSString *startDirectionString = [formatter stringFromDirection:startDirection];
+ NSString *endDirectionString = [formatter stringFromDirection:endDirection];
+ NSString *directionString = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"ROAD_DIRECTION_A11Y_FMT", nil, nil, @"%@ to %@", @"String format for accessibility value for road feature; {starting compass direction}, {ending compass direction}"), startDirectionString, endDirectionString];
+ [facts addObject:directionString];
+ }
+ }
+
+ if (facts.count) {
+ NSString *separator = NSLocalizedStringWithDefaultValue(@"LIST_SEPARATOR", nil, nil, @", ", @"List separator");
+ self.accessibilityValue = [facts componentsJoinedByString:separator];
+ }
+ }
+ return self;
+}
+
+@end
+
+@implementation MGLMapViewProxyAccessibilityElement
+
+- (instancetype)initWithAccessibilityContainer:(id)container {
+ if (self = [super initWithAccessibilityContainer:container]) {
+ self.accessibilityTraits = UIAccessibilityTraitButton;
+ self.accessibilityLabel = [self.accessibilityContainer accessibilityLabel];
+ self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"CLOSE_CALLOUT_A11Y_HINT", nil, nil, @"Returns to the map", @"Accessibility hint for closing the selected annotation’s callout view and returning to the map");
+ }
+ return self;
+}
+
+@end
diff --git a/platform/ios/src/MGLMapView+IBAdditions.h b/platform/ios/src/MGLMapView+IBAdditions.h
index 3766d044d8..d02c938c57 100644
--- a/platform/ios/src/MGLMapView+IBAdditions.h
+++ b/platform/ios/src/MGLMapView+IBAdditions.h
@@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) IBInspectable BOOL allowsRotating;
@property (nonatomic) IBInspectable BOOL allowsTilting;
@property (nonatomic) IBInspectable BOOL showsUserLocation;
+@property (nonatomic) IBInspectable BOOL showsHeading;
@end
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index ca765a046b..e2c070a54f 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -4,6 +4,7 @@
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
+#import "MGLFoundation.h"
#import "MGLTypes.h"
NS_ASSUME_NONNULL_BEGIN
@@ -23,13 +24,13 @@ NS_ASSUME_NONNULL_BEGIN
@protocol MGLFeature;
/** The default deceleration rate for a map view. */
-extern const CGFloat MGLMapViewDecelerationRateNormal;
+extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateNormal;
/** A fast deceleration rate for a map view. */
-extern const CGFloat MGLMapViewDecelerationRateFast;
+extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateFast;
/** Disables deceleration in a map view. */
-extern const CGFloat MGLMapViewDecelerationRateImmediate;
+extern MGL_EXPORT const CGFloat MGLMapViewDecelerationRateImmediate;
/**
The vertical alignment of an annotation within a map view. Used with
@@ -125,7 +126,7 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) {
ensuring that your use adheres to the relevant terms of use.
*/
-IB_DESIGNABLE
+MGL_EXPORT IB_DESIGNABLE
@interface MGLMapView : UIView
#pragma mark Creating Instances
@@ -252,6 +253,9 @@ IB_DESIGNABLE
A view showing legally required copyright notices and telemetry settings,
positioned at the bottom-right of the map view.
+ If you choose to reimplement this view, assign the `-showAttribution:` method
+ as the action for your view to present the default notices and settings.
+
@note The Mapbox terms of service, which governs the use of Mapbox-hosted
vector tiles and styles,
<a href="https://www.mapbox.com/help/attribution/">requires</a> these
@@ -271,23 +275,24 @@ IB_DESIGNABLE
@property (nonatomic, readonly) UIButton *attributionButton;
/**
- Support for style classes has been removed. This property always returns an empty array.
+ Show the attribution and telemetry action sheet.
+
+ This action is performed when the user taps on the attribution button provided
+ by default via the `attributionButton` property. If you implement a custom
+ attribution button, you should add this action to the button.
*/
+- (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.")));
-/**
- Support for style classes has been removed. This property always returns NO.
- */
+/// :nodoc: Support for style classes has been removed. This property always returns NO.
- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
-/**
- Support for style classes has been removed. This property is a no-op.
- */
+/// :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.")));
-/**
- Support for style classes has been removed. This property is a no-op.
- */
+/// :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.")));
#pragma mark Displaying the User’s Location
@@ -369,6 +374,23 @@ IB_DESIGNABLE
- (void)setUserLocationVerticalAlignment:(MGLAnnotationVerticalAlignment)alignment animated:(BOOL)animated;
/**
+ A Boolean value indicating whether the user location annotation may display a
+ permanent heading indicator.
+
+ Setting this property to `YES` causes the default user location annotation to
+ appear and always show an arrow-shaped heading indicator, if heading is
+ available. This property does not rotate the map; for that, see
+ `MGLUserTrackingModeFollowWithHeading`.
+
+ This property has no effect when `userTrackingMode` is
+ `MGLUserTrackingModeFollowWithHeading` or
+ `MGLUserTrackingModeFollowWithCourse`.
+
+ The default value of this property is `NO`.
+ */
+@property (nonatomic, assign) BOOL showsUserHeadingIndicator;
+
+/**
Whether the map view should display a heading calibration alert when necessary.
The default value is `YES`.
*/
@@ -588,7 +610,7 @@ IB_DESIGNABLE
*
* The default minimumZoomLevel is 0.
*/
-@property (nonatomic) double minimumZoomLevel;
+@property (nonatomic) IBInspectable double minimumZoomLevel;
/**
* The maximum zoom level the map can be shown at.
@@ -596,9 +618,10 @@ IB_DESIGNABLE
* If the value of this property is smaller than that of the
* minimumZoomLevel property, the behavior is undefined.
*
- * The default maximumZoomLevel is 20.
+ * The default maximumZoomLevel is 22. The upper bound for this property
+ * is 25.5.
*/
-@property (nonatomic) double maximumZoomLevel;
+@property (nonatomic) IBInspectable double maximumZoomLevel;
/**
The heading of the map, measured in degrees clockwise from true north.
@@ -776,6 +799,23 @@ IB_DESIGNABLE
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
/**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition duration and timing function.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously.
+ @param function A timing function used for the animation. Set this parameter to
+ `nil` for a transition that matches most system animations. If the duration
+ is `0`, this parameter is ignored.
+ @param edgePadding The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(UIEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion;
+
+/**
Moves the viewpoint to a different location using a transition animation that
evokes powered flight and a default duration based on the length of the flight
path.
@@ -850,6 +890,20 @@ IB_DESIGNABLE
- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets;
/**
+ Returns the camera that best fits the given shape, with the specified direction,
+ optionally with some additional padding on each side.
+
+ @param shape The shape to fit to the receiver’s viewport.
+ @param direction The direction of the viewport, measured in degrees clockwise from true north.
+ @param insets The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @return A camera object centered on the shape's center with zoom level as high
+ (close to the ground) as possible while still including the entire shape. The
+ camera object uses the current pitch.
+ */
+- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets;
+
+/**
Returns the point in this view’s coordinate system on which to "anchor" in
response to a user-initiated gesture.
@@ -985,16 +1039,6 @@ IB_DESIGNABLE
@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations;
/**
- The complete list of annotations associated with the receiver that are
- currently visible.
-
- The objects in this array must adopt the `MGLAnnotation` protocol. If no
- 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;
-
-/**
Adds an annotation to the map view.
@note `MGLMultiPolyline`, `MGLMultiPolygon`, `MGLShapeCollection`, and
@@ -1089,6 +1133,16 @@ IB_DESIGNABLE
- (nullable __kindof MGLAnnotationView *)dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier;
/**
+ The complete list of annotations associated with the receiver that are
+ currently visible.
+
+ The objects in this array must adopt the `MGLAnnotation` protocol. If no
+ 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;
+
+/**
Returns the list of annotations associated with the receiver that intersect with
the given rectangle.
@@ -1257,6 +1311,11 @@ IB_DESIGNABLE
`-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and
`-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources.
+ The returned features may also include features corresponding to annotations.
+ These features are not object-equal to the `MGLAnnotation` objects that were
+ originally added to the map. To query the map for annotations, use
+ `visibleAnnotations` or `-[MGLMapView visibleAnnotationsInRect:]`.
+
@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
the style URL to an explicitly versioned style using a convenience method
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 6c6d8d2980..8dec9e520d 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -6,7 +6,6 @@
#import <OpenGLES/EAGL.h>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/view.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/mode.hpp>
@@ -19,8 +18,10 @@
#include <mbgl/style/image.hpp>
#include <mbgl/style/transition_options.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
-#include <mbgl/map/backend.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/mode.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/math/wrap.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/geo.hpp>
@@ -35,11 +36,15 @@
#include <mbgl/util/projection.hpp>
#import "Mapbox.h"
+#import "MGLShape_Private.h"
#import "MGLFeature_Private.h"
#import "MGLGeometry_Private.h"
#import "MGLMultiPoint_Private.h"
#import "MGLOfflineStorage_Private.h"
+#import "MGLVectorSource_Private.h"
#import "MGLFoundation_Private.h"
+#import "MGLRendererFrontend.h"
+#import "MGLRendererConfiguration.h"
#import "NSBundle+MGLAdditions.h"
#import "NSDate+MGLAdditions.h"
@@ -66,6 +71,7 @@
#import "MGLAnnotationContainerView.h"
#import "MGLAnnotationContainerView_Private.h"
#import "MGLAttributionInfo_Private.h"
+#import "MGLMapAccessibilityElement.h"
#include <algorithm>
#include <cstdlib>
@@ -85,6 +91,8 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) {
MGLUserTrackingStatePossible = 0,
/// The map view has begun to move to the first reported user location.
MGLUserTrackingStateBegan,
+ /// The map view begins a significant transition.
+ MGLUserTrackingStateBeginSignificantTransition,
/// The map view has finished moving to the first reported user location.
MGLUserTrackingStateChanged,
};
@@ -113,6 +121,9 @@ const NSUInteger MGLTargetFrameInterval = 1; // Target FPS will be 60 divided b
/// Tolerance for snapping to true north, measured in degrees in either direction.
const CLLocationDirection MGLToleranceForSnappingToNorth = 7;
+/// Distance threshold to stop the camera while animating.
+const CLLocationDistance MGLDistanceThresholdForCameraPause = 500;
+
/// Reuse identifier and file name of the default point annotation image.
static NSString * const MGLDefaultStyleMarkerSymbolName = @"default_marker";
@@ -132,12 +143,6 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1;
const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10);
-// Context for KVO observing UILayoutGuides.
-static void * MGLLayoutGuidesUpdatedContext = &MGLLayoutGuidesUpdatedContext;
-
-/// Unique identifier representing a single annotation in mbgl.
-typedef uint32_t MGLAnnotationTag;
-
/// An indication that the requested annotation was not found or is nonexistent.
enum { MGLAnnotationTagNotFound = UINT32_MAX };
@@ -160,38 +165,6 @@ mbgl::util::UnitBezier MGLUnitBezierForMediaTimingFunction(CAMediaTimingFunction
return { p1[0], p1[1], p2[0], p2[1] };
}
-@interface MGLAnnotationAccessibilityElement : UIAccessibilityElement
-
-@property (nonatomic) MGLAnnotationTag tag;
-
-- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)identifier NS_DESIGNATED_INITIALIZER;
-
-@end
-
-@implementation MGLAnnotationAccessibilityElement
-
-- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)tag
-{
- if (self = [super initWithAccessibilityContainer:container])
- {
- _tag = tag;
- self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitAdjustable;
- }
- return self;
-}
-
-- (void)accessibilityIncrement
-{
- [self.accessibilityContainer accessibilityIncrement];
-}
-
-- (void)accessibilityDecrement
-{
- [self.accessibilityContainer accessibilityDecrement];
-}
-
-@end
-
/// Lightweight container for metadata about an annotation, including the annotation itself.
class MGLAnnotationContext {
public:
@@ -203,32 +176,12 @@ public:
NSString *viewReuseIdentifier;
};
-/** An accessibility element representing the MGLMapView at large. */
-@interface MGLMapViewProxyAccessibilityElement : UIAccessibilityElement
-
-@end
-
-@implementation MGLMapViewProxyAccessibilityElement
-
-- (instancetype)initWithAccessibilityContainer:(id)container
-{
- if (self = [super initWithAccessibilityContainer:container])
- {
- self.accessibilityTraits = UIAccessibilityTraitButton;
- self.accessibilityLabel = [self.accessibilityContainer accessibilityLabel];
- self.accessibilityHint = NSLocalizedStringWithDefaultValue(@"CLOSE_CALLOUT_A11Y_HINT", nil, nil, @"Returns to the map", @"Accessibility hint for closing the selected annotation’s callout view and returning to the map");
- }
- return self;
-}
-
-@end
-
#pragma mark - Private -
@interface MGLMapView () <UIGestureRecognizerDelegate,
GLKViewDelegate,
CLLocationManagerDelegate,
- SMCalloutViewDelegate,
+ MGLSMCalloutViewDelegate,
MGLCalloutViewDelegate,
MGLMultiPointDelegate,
MGLAnnotationImageDelegate>
@@ -236,11 +189,18 @@ public:
@property (nonatomic) EAGLContext *context;
@property (nonatomic) GLKView *glView;
@property (nonatomic) UIImageView *glSnapshotView;
+
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints;
@property (nonatomic, readwrite) MGLScaleBar *scaleBar;
@property (nonatomic, readwrite) UIImageView *compassView;
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *compassViewConstraints;
@property (nonatomic, readwrite) UIImageView *logoView;
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints;
@property (nonatomic, readwrite) UIButton *attributionButton;
+@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints;
+
@property (nonatomic, readwrite) MGLStyle *style;
+
@property (nonatomic) UITapGestureRecognizer *singleTapGestureRecognizer;
@property (nonatomic) UITapGestureRecognizer *doubleTap;
@property (nonatomic) UITapGestureRecognizer *twoFingerTap;
@@ -249,11 +209,14 @@ public:
@property (nonatomic) UIRotationGestureRecognizer *rotate;
@property (nonatomic) UILongPressGestureRecognizer *quickZoom;
@property (nonatomic) UIPanGestureRecognizer *twoFingerDrag;
+
/// Mapping from reusable identifiers to annotation images.
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier;
+
/// Currently shown popover representing the selected annotation.
@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
@property (nonatomic) MGLUserLocationAnnotationView *userLocationAnnotationView;
+
/// Indicates how thoroughly the map view is tracking the user location.
@property (nonatomic) MGLUserTrackingState userTrackingState;
@property (nonatomic) CLLocationManager *locationManager;
@@ -273,6 +236,8 @@ public:
{
mbgl::Map *_mbglMap;
MBGLView *_mbglView;
+ std::unique_ptr<MGLRenderFrontend> _rendererFrontend;
+
std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool;
BOOL _opaque;
@@ -294,8 +259,6 @@ public:
NSDate *_userLocationAnimationCompletionDate;
/// True if a willChange notification has been issued for shape annotation layers and a didChange notification is pending.
BOOL _isChangingAnnotationLayers;
- BOOL _isObservingTopLayoutGuide;
- BOOL _isObservingBottomLayoutGuide;
BOOL _isWaitingForRedundantReachableNotification;
BOOL _isTargetingInterfaceBuilder;
@@ -310,6 +273,8 @@ public:
/// Center coordinate of the pinch gesture on the previous iteration of the gesture.
CLLocationCoordinate2D _previousPinchCenterCoordinate;
NSUInteger _previousPinchNumberOfTouches;
+
+ CLLocationDistance _distanceFromOldUserLocation;
BOOL _delegateHasAlphasForShapeAnnotations;
BOOL _delegateHasStrokeColorsForShapeAnnotations;
@@ -317,6 +282,10 @@ public:
BOOL _delegateHasLineWidthsForShapeAnnotations;
MGLCompassDirectionFormatter *_accessibilityCompassFormatter;
+ NS_ARRAY_OF(id <MGLFeature>) *_visiblePlaceFeatures;
+ NS_ARRAY_OF(id <MGLFeature>) *_visibleRoadFeatures;
+ NS_MUTABLE_SET_OF(MGLFeatureAccessibilityElement *) *_featureAccessibilityElements;
+ BOOL _accessibilityValueAnnouncementIsPending;
MGLReachability *_reachability;
}
@@ -355,7 +324,7 @@ public:
+ (void)initialize
{
- if (self == [MGLMapView self])
+ if (self == [MGLMapView class])
{
[MGLSDKUpdateChecker checkForUpdates];
}
@@ -403,6 +372,11 @@ public:
return _mbglMap;
}
+- (mbgl::Renderer *)renderer
+{
+ return _rendererFrontend->getRenderer();
+}
+
- (void)commonInit
{
_isTargetingInterfaceBuilder = NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent;
@@ -421,10 +395,9 @@ public:
self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction | UIAccessibilityTraitAdjustable;
_accessibilityCompassFormatter = [[MGLCompassDirectionFormatter alloc] init];
_accessibilityCompassFormatter.unitStyle = NSFormattingUnitStyleLong;
-
self.backgroundColor = [UIColor clearColor];
self.clipsToBounds = YES;
-
+ if (@available(iOS 11.0, *)) { self.accessibilityIgnoresInvertColors = YES; }
// setup mbgl view
_mbglView = new MBGLView(self);
@@ -434,11 +407,12 @@ public:
[[NSFileManager defaultManager] removeItemAtPath:fileCachePath error:NULL];
// setup mbgl map
- mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
- const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
+ MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration];
_mbglThreadPool = mbgl::sharedThreadPool();
- _mbglMap = new mbgl::Map(*_mbglView, self.size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
- [self validateTileCacheSize];
+
+ auto renderer = std::make_unique<mbgl::Renderer>(*_mbglView, config.scaleFactor, *config.fileSource, *_mbglThreadPool, config.contextMode, config.cacheDir, config.localFontFamilyName);
+ _rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, *_mbglView);
+ _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, config.scaleFactor, *[config fileSource], *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
// start paused if in IB
if (_isTargetingInterfaceBuilder || background) {
@@ -466,23 +440,30 @@ public:
_selectedAnnotationTag = MGLAnnotationTagNotFound;
_annotationsNearbyLastTap = {};
- // setup logo bug
+ // setup logo
//
UIImage *logo = [MGLMapView resourceImageNamed:@"mapbox"];
_logoView = [[UIImageView alloc] initWithImage:logo];
_logoView.accessibilityTraits = UIAccessibilityTraitStaticText;
_logoView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"LOGO_A11Y_LABEL", nil, nil, @"Mapbox", @"Accessibility label");
+ _logoView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_logoView];
+ _logoViewConstraints = [NSMutableArray array];
// setup attribution
//
_attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
_attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label");
_attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint");
- [_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside];
+ [_attributionButton addTarget:self action:@selector(showAttribution:) forControlEvents:UIControlEventTouchUpInside];
+ _attributionButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_attributionButton];
+ _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];
@@ -492,12 +473,16 @@ public:
_compassView.accessibilityTraits = UIAccessibilityTraitButton;
_compassView.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_LABEL", nil, nil, @"Compass", @"Accessibility label");
_compassView.accessibilityHint = NSLocalizedStringWithDefaultValue(@"COMPASS_A11Y_HINT", nil, nil, @"Rotates the map to face due north", @"Accessibility hint");
+ _compassView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_compassView];
+ _compassViewConstraints = [NSMutableArray array];
// setup scale control
//
_scaleBar = [[MGLScaleBar alloc] init];
+ _scaleBar.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_scaleBar];
+ _scaleBarConstraints = [NSMutableArray array];
// setup interaction
//
@@ -526,21 +511,21 @@ public:
_singleTapGestureRecognizer.delegate = self;
[self addGestureRecognizer:_singleTapGestureRecognizer];
- _twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTapGesture:)];
- _twoFingerTap.numberOfTouchesRequired = 2;
- [_twoFingerTap requireGestureRecognizerToFail:_pinch];
- [_twoFingerTap requireGestureRecognizerToFail:_rotate];
- [self addGestureRecognizer:_twoFingerTap];
-
_twoFingerDrag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDragGesture:)];
_twoFingerDrag.minimumNumberOfTouches = 2;
_twoFingerDrag.maximumNumberOfTouches = 2;
_twoFingerDrag.delegate = self;
- [_twoFingerDrag requireGestureRecognizerToFail:_twoFingerTap];
[_twoFingerDrag requireGestureRecognizerToFail:_pan];
[self addGestureRecognizer:_twoFingerDrag];
_pitchEnabled = YES;
+ _twoFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerTapGesture:)];
+ _twoFingerTap.numberOfTouchesRequired = 2;
+ [_twoFingerTap requireGestureRecognizerToFail:_pinch];
+ [_twoFingerTap requireGestureRecognizerToFail:_rotate];
+ [_twoFingerTap requireGestureRecognizerToFail:_twoFingerDrag];
+ [self addGestureRecognizer:_twoFingerTap];
+
_decelerationRate = MGLMapViewDecelerationRateNormal;
_quickZoom = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleQuickZoomGesture:)];
@@ -610,6 +595,7 @@ public:
_glView.contentScaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
_glView.layer.opaque = _opaque;
_glView.delegate = self;
+
[_glView bindDrawable];
[self insertSubview:_glView atIndex:0];
_glView.contentMode = UIViewContentModeCenter;
@@ -657,48 +643,6 @@ public:
_isWaitingForRedundantReachableNotification = NO;
}
-- (void)willMoveToWindow:(UIWindow *)newWindow
-{
- [super willMoveToWindow:newWindow];
-
- if (newWindow) {
- [self addLayoutGuideObserversIfNeeded];
- } else {
- [self removeLayoutGuideObserversIfNeeded];
- }
-}
-
-- (void)addLayoutGuideObserversIfNeeded
-{
- UIViewController *viewController = self.viewControllerForLayoutGuides;
- BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets;
-
- if (useLayoutGuides && viewController.topLayoutGuide && !_isObservingTopLayoutGuide) {
- [(NSObject *)viewController.topLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext];
- _isObservingTopLayoutGuide = YES;
- }
-
- if (useLayoutGuides && viewController.bottomLayoutGuide && !_isObservingBottomLayoutGuide) {
- [(NSObject *)viewController.bottomLayoutGuide addObserver:self forKeyPath:@"bounds" options:0 context:(void *)&MGLLayoutGuidesUpdatedContext];
- _isObservingBottomLayoutGuide = YES;
- }
-}
-
-- (void)removeLayoutGuideObserversIfNeeded
-{
- UIViewController *viewController = self.viewControllerForLayoutGuides;
-
- if (_isObservingTopLayoutGuide) {
- [(NSObject *)viewController.topLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext];
- _isObservingTopLayoutGuide = NO;
- }
-
- if (_isObservingBottomLayoutGuide) {
- [(NSObject *)viewController.bottomLayoutGuide removeObserver:self forKeyPath:@"bounds" context:(void *)&MGLLayoutGuidesUpdatedContext];
- _isObservingBottomLayoutGuide = NO;
- }
-}
-
- (void)dealloc
{
[_reachability stopNotifier];
@@ -707,8 +651,6 @@ public:
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_attributionButton removeObserver:self forKeyPath:@"hidden"];
- [self removeLayoutGuideObserversIfNeeded];
-
// Removing the annotations unregisters any outstanding KVO observers.
NSArray *annotations = self.annotations;
if (annotations)
@@ -734,6 +676,18 @@ public:
{
[EAGLContext setCurrentContext:nil];
}
+
+ [self.compassViewConstraints removeAllObjects];
+ self.compassViewConstraints = nil;
+
+ [self.scaleBarConstraints removeAllObjects];
+ self.scaleBarConstraints = nil;
+
+ [self.logoViewConstraints removeAllObjects];
+ self.logoViewConstraints = nil;
+
+ [self.attributionButtonConstraints removeAllObjects];
+ self.attributionButtonConstraints = nil;
}
- (void)setDelegate:(nullable id<MGLMapViewDelegate>)delegate
@@ -752,47 +706,11 @@ public:
{
MGLAssertIsMainThread();
- _mbglMap->onLowMemory();
+ _rendererFrontend->onLowMemory();
}
#pragma mark - Layout -
-- (void)setFrame:(CGRect)frame
-{
- [super setFrame:frame];
- if ( ! CGRectEqualToRect(frame, self.frame))
- {
- [self validateTileCacheSize];
- }
-}
-
-- (void)setBounds:(CGRect)bounds
-{
- [super setBounds:bounds];
- if ( ! CGRectEqualToRect(bounds, self.bounds))
- {
- [self validateTileCacheSize];
- }
-}
-
-- (void)validateTileCacheSize
-{
- if ( ! _mbglMap)
- {
- return;
- }
-
- CGFloat zoomFactor = self.maximumZoomLevel - self.minimumZoomLevel + 1;
- CGFloat cpuFactor = [NSProcessInfo processInfo].processorCount;
- CGFloat memoryFactor = (CGFloat)[NSProcessInfo processInfo].physicalMemory / 1000 / 1000 / 1000;
- CGFloat sizeFactor = (CGRectGetWidth(self.bounds) / mbgl::util::tileSize) *
- (CGRectGetHeight(self.bounds) / mbgl::util::tileSize);
-
- NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
-
- _mbglMap->setSourceTileCacheSize(cacheSize);
-}
-
+ (BOOL)requiresConstraintBasedLayout
{
return YES;
@@ -816,15 +734,207 @@ public:
return nil;
}
+- (void)updateConstraintsPreiOS11 {
+ // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets
+ // is set to YES, use its view as the parent for constraints. -[MGLMapView adjustContentInset]
+ // already take top and bottom layout guides into account. If we don't have a reference, apply
+ // constraints against ourself to maintain placement of the subviews.
+ //
+ UIViewController *viewController = self.viewControllerForLayoutGuides;
+ BOOL useLayoutGuides = viewController.view && viewController.automaticallyAdjustsScrollViewInsets;
+ UIView *containerView = useLayoutGuides ? viewController.view : self;
+
+ // compass view
+ //
+ [containerView removeConstraints:self.compassViewConstraints];
+ [self.compassViewConstraints removeAllObjects];
+
+ if (useLayoutGuides) {
+ [self.compassViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.compassView
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:viewController.topLayoutGuide
+ attribute:NSLayoutAttributeBottom
+ multiplier:1.0
+ constant:5.0 + self.contentInset.top]];
+ }
+ [self.compassViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.compassView
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self
+ attribute:NSLayoutAttributeTop
+ multiplier:1.0
+ constant:5.0 + self.contentInset.top]];
+ [self.compassViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeTrailing
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.compassView
+ attribute:NSLayoutAttributeTrailing
+ multiplier:1.0
+ constant:5.0 + self.contentInset.right]];
+
+ [containerView addConstraints:self.compassViewConstraints];
+
+ // scale bar view
+ //
+ [containerView removeConstraints:self.scaleBarConstraints];
+ [self.scaleBarConstraints removeAllObjects];
+
+ if (useLayoutGuides) {
+ [self.scaleBarConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.scaleBar
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:viewController.topLayoutGuide
+ attribute:NSLayoutAttributeBottom
+ multiplier:1.0
+ constant:5.0 + self.contentInset.top]];
+ }
+ [self.scaleBarConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.scaleBar
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self
+ attribute:NSLayoutAttributeTop
+ multiplier:1.0
+ constant:5.0 + self.contentInset.top]];
+ [self.scaleBarConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.scaleBar
+ attribute:NSLayoutAttributeLeft
+ relatedBy:NSLayoutRelationEqual
+ toItem:self
+ attribute:NSLayoutAttributeLeft
+ multiplier:1.0
+ constant:8.0 + self.contentInset.left]];
+
+ [containerView addConstraints:self.scaleBarConstraints];
+
+ // logo view
+ //
+ [containerView removeConstraints:self.logoViewConstraints];
+ [self.logoViewConstraints removeAllObjects];
+
+ if (useLayoutGuides) {
+ [self.logoViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.logoView
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1.0
+ constant:8.0 + self.contentInset.bottom]];
+ }
+ [self.logoViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.logoView
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8 + self.contentInset.bottom]];
+ [self.logoViewConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self.logoView
+ attribute:NSLayoutAttributeLeading
+ relatedBy:NSLayoutRelationEqual
+ toItem:self
+ attribute:NSLayoutAttributeLeading
+ multiplier:1.0
+ constant:8.0 + self.contentInset.left]];
+ [containerView addConstraints:self.logoViewConstraints];
+
+ // attribution button
+ //
+ [containerView removeConstraints:self.attributionButtonConstraints];
+ [self.attributionButtonConstraints removeAllObjects];
+
+ if (useLayoutGuides) {
+ [self.attributionButtonConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:viewController.bottomLayoutGuide
+ attribute:NSLayoutAttributeTop
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8 + self.contentInset.bottom]];
+ }
+ [self.attributionButtonConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeBottom
+ relatedBy:NSLayoutRelationGreaterThanOrEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeBaseline
+ multiplier:1
+ constant:8 + self.contentInset.bottom]];
+
+ [self.attributionButtonConstraints addObject:
+ [NSLayoutConstraint constraintWithItem:self
+ attribute:NSLayoutAttributeTrailing
+ relatedBy:NSLayoutRelationEqual
+ toItem:self.attributionButton
+ attribute:NSLayoutAttributeTrailing
+ multiplier:1
+ constant:8 + self.contentInset.right]];
+ [containerView addConstraints:self.attributionButtonConstraints];
+}
+
- (void)updateConstraints
{
- [super updateConstraints];
- // If we have a view controller reference and its automaticallyAdjustsScrollViewInsets
- // is set to YES, -[MGLMapView adjustContentInset] takes top and bottom layout
- // guides into account. To get notified about changes to the layout guides,
- // we need to observe their bounds and re-layout accordingly.
- [self addLayoutGuideObserversIfNeeded];
+// If compiling with the iOS 11+ SDK
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+ // If safeAreaLayoutGuide API exists
+ if ( [self respondsToSelector:@selector(safeAreaLayoutGuide)] ) {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+ UILayoutGuide *safeAreaLayoutGuide = self.safeAreaLayoutGuide;
+#pragma clang diagnostic pop
+ // compass view
+ [self removeConstraints:self.compassViewConstraints];
+ [self.compassViewConstraints removeAllObjects];
+ [self.compassViewConstraints addObject:[self.compassView.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor
+ constant:5.0 + self.contentInset.top]];
+ [self.compassViewConstraints addObject:[safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:self.compassView.rightAnchor
+ constant:8.0 + self.contentInset.right]];
+ [self addConstraints:self.compassViewConstraints];
+
+ // scale bar view
+ [self removeConstraints:self.scaleBarConstraints];
+ [self.scaleBarConstraints removeAllObjects];
+ [self.scaleBarConstraints addObject:[self.scaleBar.topAnchor constraintEqualToAnchor:safeAreaLayoutGuide.topAnchor
+ constant:5.0 + self.contentInset.top]];
+ [self.scaleBarConstraints addObject:[self.scaleBar.leftAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leftAnchor
+ constant:8.0 + self.contentInset.left]];
+ [self addConstraints:self.scaleBarConstraints];
+
+ // logo view
+ [self removeConstraints:self.logoViewConstraints];
+ [self.logoViewConstraints removeAllObjects];
+ [self.logoViewConstraints addObject:[safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:self.logoView.bottomAnchor
+ constant:8.0 + self.contentInset.bottom]];
+ [self.logoViewConstraints addObject:[self.logoView.leftAnchor constraintEqualToAnchor:safeAreaLayoutGuide.leftAnchor
+ constant:8.0 + self.contentInset.left]];
+ [self addConstraints:self.logoViewConstraints];
+
+ // attribution button
+ [self removeConstraints:self.attributionButtonConstraints];
+ [self.attributionButtonConstraints removeAllObjects];
+ [self.attributionButtonConstraints addObject:[safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:self.attributionButton.bottomAnchor
+ constant:8.0 + self.contentInset.bottom]];
+ [self.attributionButtonConstraints addObject:[safeAreaLayoutGuide.rightAnchor constraintEqualToAnchor:self.attributionButton.rightAnchor
+ constant:8.0 + self.contentInset.right]];
+ [self addConstraints:self.attributionButtonConstraints];
+ } else {
+ [self updateConstraintsPreiOS11];
+ }
+#else
+ [self updateConstraintsPreiOS11];
+#endif
+
+ [super updateConstraints];
}
- (BOOL)isOpaque
@@ -840,12 +950,9 @@ public:
// This is the delegate of the GLKView object's display call.
- (void)glkView:(__unused GLKView *)view drawInRect:(__unused CGRect)rect
{
- if ( ! self.dormant)
+ if ( ! self.dormant || ! _rendererFrontend)
{
- // The OpenGL implementation automatically enables the OpenGL context for us.
- mbgl::BackendScope scope { *_mbglView, mbgl::BackendScope::ScopeType::Implicit };
-
- _mbglMap->render(*_mbglView);
+ _rendererFrontend->render();
[self updateUserLocationAnnotationView];
}
@@ -854,11 +961,17 @@ public:
// This gets called when the view dimension changes, e.g. because the device is being rotated.
- (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
+ // 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.
+ [self.scaleBar invalidateIntrinsicContentSize];
+
[super layoutSubviews];
[self adjustContentInset];
-
- [self layoutOrnaments];
if (!_isTargetingInterfaceBuilder) {
_mbglMap->setSize([self size]);
@@ -866,44 +979,15 @@ public:
if (self.compassView.alpha)
{
- [self updateHeadingForDeviceOrientation];
[self updateCompass];
}
- [self updateUserLocationAnnotationView];
-}
+ if (self.compassView.alpha || self.showsUserHeadingIndicator)
+ {
+ [self updateHeadingForDeviceOrientation];
+ }
-- (void)layoutOrnaments
-{
- // scale bar
- self.scaleBar.frame = {
- self.contentInset.left+8,
- self.contentInset.top+5,
- CGRectGetWidth(self.scaleBar.frame),
- CGRectGetHeight(self.scaleBar.frame)
- };
-
- // compass
- self.compassView.center = {
- .x = CGRectGetWidth(self.bounds)-CGRectGetMidX(self.compassView.bounds)-self.contentInset.right-5,
- .y = CGRectGetMidY(self.compassView.bounds)+self.contentInset.top+5
- };
-
- // logo bug
- self.logoView.frame = {
- self.contentInset.left+8,
- CGRectGetHeight(self.bounds)-8-self.contentInset.bottom-CGRectGetHeight(self.logoView.bounds),
- CGRectGetWidth(self.logoView.bounds),
- CGRectGetHeight(self.logoView.bounds)
- };
-
- // attribution
- self.attributionButton.frame = {
- CGRectGetWidth(self.bounds)-CGRectGetWidth(self.attributionButton.bounds)-self.contentInset.right-8,
- CGRectGetHeight(self.bounds)-CGRectGetHeight(self.attributionButton.bounds)-self.contentInset.bottom-8,
- CGRectGetWidth(self.attributionButton.bounds),
- CGRectGetHeight(self.attributionButton.bounds)
- };
+ [self updateUserLocationAnnotationView];
}
/// Updates `contentInset` to reflect the current window geometry.
@@ -975,7 +1059,7 @@ public:
}
// Compass, logo and attribution button constraints needs to be updated.
- [self setNeedsLayout];
+ [self setNeedsUpdateConstraints];
}
/// Returns the frame of inset content within the map view.
@@ -1138,8 +1222,10 @@ public:
- (void)updateTintColorForView:(UIView *)view
{
- // stop at recursing container & annotation views (#8522)
- if ([view isEqual:self.annotationContainerView]) return;
+ // Don't update:
+ // - annotation views
+ // - attribution button (handled automatically)
+ if ([view isEqual:self.annotationContainerView] || [view isEqual:self.attributionButton]) return;
if ([view respondsToSelector:@selector(setTintColor:)]) view.tintColor = self.tintColor;
@@ -1167,6 +1253,10 @@ public:
{
_changeDelimiterSuppressionDepth = 0;
_mbglMap->setGestureInProgress(false);
+ if (self.userTrackingState == MGLUserTrackingStateBegan)
+ {
+ [self setUserTrackingMode:MGLUserTrackingModeNone animated:NO];
+ }
_mbglMap->cancelTransitions();
}
@@ -1268,8 +1358,6 @@ public:
{
if ( ! self.isZoomEnabled) return;
- if (_mbglMap->getZoom() <= _mbglMap->getMinZoom() && pinch.scale < 1) return;
-
_mbglMap->cancelTransitions();
CGPoint centerPoint = [self anchorPointForGesture:pinch];
@@ -1285,27 +1373,27 @@ public:
}
else if (pinch.state == UIGestureRecognizerStateChanged)
{
+ // Zoom limiting happens at the core level.
CGFloat newScale = self.scale * pinch.scale;
- double zoom = log2(newScale);
- if (zoom < _mbglMap->getMinZoom()) return;
-
+ double newZoom = log2(newScale);
+
// Calculates the final camera zoom, has no effect within current map camera.
- MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:centerPoint];
+ MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:newZoom aroundAnchorPoint:centerPoint];
if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
[self.delegate mapView:self shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- _mbglMap->setZoom(zoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
- }
- // The gesture recognizer only reports the gesture’s current center
- // point, so use the previous center point to anchor the transition.
- // If the number of touches has changed, the remembered center point is
- // meaningless.
- if (self.userTrackingMode == MGLUserTrackingModeNone && pinch.numberOfTouches == _previousPinchNumberOfTouches)
- {
- CLLocationCoordinate2D centerCoordinate = _previousPinchCenterCoordinate;
- _mbglMap->setLatLng(MGLLatLngFromLocationCoordinate2D(centerCoordinate),
- mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ _mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ // The gesture recognizer only reports the gesture’s current center
+ // point, so use the previous center point to anchor the transition.
+ // If the number of touches has changed, the remembered center point is
+ // meaningless.
+ if (self.userTrackingMode == MGLUserTrackingModeNone && pinch.numberOfTouches == _previousPinchNumberOfTouches)
+ {
+ CLLocationCoordinate2D centerCoordinate = _previousPinchCenterCoordinate;
+ _mbglMap->setLatLng(MGLLatLngFromLocationCoordinate2D(centerCoordinate),
+ mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ }
}
[self cameraIsChanging];
}
@@ -1473,7 +1561,9 @@ public:
id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:singleTap persistingResults:YES];
if(annotation)
{
- [self selectAnnotation:annotation animated:YES];
+ CGPoint calloutPoint = [singleTap locationInView:self];
+ CGRect positionRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:calloutPoint];
+ [self selectAnnotation:annotation animated:YES calloutPositioningRect:positionRect];
}
else
{
@@ -1643,9 +1733,9 @@ public:
{
CGFloat distance = [quickZoom locationInView:quickZoom.view].y - self.quickZoomStart;
- CGFloat newZoom = log2f(self.scale) + (distance / 75);
+ CGFloat newZoom = MAX(log2f(self.scale) + (distance / 75), _mbglMap->getMinZoom());
- if (newZoom < _mbglMap->getMinZoom()) return;
+ if (_mbglMap->getZoom() == newZoom) return;
CGPoint centerPoint = [self anchorPointForGesture:quickZoom];
@@ -1672,14 +1762,14 @@ public:
if ( ! self.isPitchEnabled) return;
_mbglMap->cancelTransitions();
- MGLMapCamera *oldCamera = self.camera;
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag];
[self notifyGestureDidBegin];
}
- else if (twoFingerDrag.state == UIGestureRecognizerStateBegan || twoFingerDrag.state == UIGestureRecognizerStateChanged)
+
+ if (twoFingerDrag.state == UIGestureRecognizerStateBegan || twoFingerDrag.state == UIGestureRecognizerStateChanged)
{
CGFloat gestureDistance = CGPoint([twoFingerDrag translationInView:twoFingerDrag.view]).y;
CGFloat currentPitch = _mbglMap->getPitch();
@@ -1689,6 +1779,7 @@ public:
CGPoint centerPoint = [self anchorPointForGesture:twoFingerDrag];
+ MGLMapCamera *oldCamera = self.camera;
MGLMapCamera *toCamera = [self cameraByTiltingToPitch:pitchNew];
if (![self.delegate respondsToSelector:@selector(mapView:shouldChangeFromCamera:toCamera:)] ||
@@ -1777,39 +1868,6 @@ public:
return [gesture locationInView:gesture.view];
}
-- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
-{
- if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
- {
- UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer;
-
- if (panGesture.minimumNumberOfTouches == 2)
- {
- CGPoint velocity = [panGesture velocityInView:panGesture.view];
- double gestureAngle = MGLDegreesFromRadians(atan(velocity.y / velocity.x));
- double horizontalToleranceDegrees = 20.0;
-
- // cancel if gesture angle is not 90º±20º (more or less vertical)
- if ( ! (fabs((fabs(gestureAngle) - 90.0)) < horizontalToleranceDegrees))
- {
- return NO;
- }
- }
- }
- else if (gestureRecognizer == _singleTapGestureRecognizer)
- {
- // Gesture will be recognized if it could deselect an annotation
- if(!self.selectedAnnotation)
- {
- id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer persistingResults:NO];
- if(!annotation) {
- return NO;
- }
- }
- }
- return YES;
-}
-
- (void)handleCalloutAccessoryTapGesture:(UITapGestureRecognizer *)tap
{
if ([self.delegate respondsToSelector:@selector(mapView:annotation:calloutAccessoryControlTapped:)])
@@ -1827,7 +1885,7 @@ public:
return [self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)];
}
-- (void)calloutViewClicked:(__unused SMCalloutView *)calloutView
+- (void)calloutViewClicked:(__unused MGLSMCalloutView *)calloutView
{
if ([self.delegate respondsToSelector:@selector(mapView:tapOnCalloutForAnnotation:)])
{
@@ -1853,6 +1911,44 @@ public:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, calloutView);
}
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
+{
+ if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
+ {
+ UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer;
+
+ if (panGesture.minimumNumberOfTouches == 2)
+ {
+ CGPoint west = [panGesture locationOfTouch:0 inView:panGesture.view];
+ CGPoint east = [panGesture locationOfTouch:1 inView:panGesture.view];
+
+ if (west.x > east.x) {
+ CGPoint swap = west;
+ west = east;
+ east = swap;
+ }
+
+ CLLocationDegrees horizontalToleranceDegrees = 60.0;
+ if ([self angleBetweenPoints:west east:east] > horizontalToleranceDegrees) {
+ return NO;
+ }
+
+ }
+ }
+ else if (gestureRecognizer == _singleTapGestureRecognizer)
+ {
+ // Gesture will be recognized if it could deselect an annotation
+ if(!self.selectedAnnotation)
+ {
+ id<MGLAnnotation>annotation = [self annotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer persistingResults:NO];
+ if(!annotation) {
+ return NO;
+ }
+ }
+ }
+ return YES;
+}
+
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSArray *validSimultaneousGestures = @[ self.pan, self.pinch, self.rotate ];
@@ -1860,6 +1956,16 @@ public:
return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]);
}
+- (CLLocationDegrees)angleBetweenPoints:(CGPoint)west east:(CGPoint)east
+{
+ CGFloat slope = (west.y - east.y) / (west.x - east.x);
+
+ CGFloat angle = atan(fabs(slope));
+ CLLocationDegrees degrees = MGLDegreesFromRadians(angle);
+
+ return degrees;
+}
+
- (void)trackGestureEvent:(NSString *)gestureID forRecognizer:(UIGestureRecognizer *)recognizer
{
CGPoint pointInView = CGPointMake([recognizer locationInView:recognizer.view].x, [recognizer locationInView:recognizer.view].y);
@@ -1876,13 +1982,28 @@ public:
#pragma mark - Attribution -
-- (void)showAttribution
+- (void)showAttribution:(id)sender
{
- NSString *title = NSLocalizedStringWithDefaultValue(@"SDK_NAME", nil, nil, @"Mapbox iOS SDK", @"Action sheet title");
+ 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)
@@ -2045,10 +2166,6 @@ public:
[self updateCalloutView];
}
}
- else if (context == MGLLayoutGuidesUpdatedContext && [keyPath isEqualToString:@"bounds"])
- {
- [self setNeedsLayout];
- }
}
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled
@@ -2153,10 +2270,11 @@ public:
- (void)resetPosition
{
- CGFloat pitch = _mbglMap->getStyle().getDefaultPitch();
- CLLocationDirection heading = mbgl::util::wrap(_mbglMap->getStyle().getDefaultBearing(), 0., 360.);
- CLLocationDistance distance = MGLAltitudeForZoomLevel(_mbglMap->getStyle().getDefaultZoom(), pitch, 0, self.frame.size);
- self.camera = [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(_mbglMap->getStyle().getDefaultLatLng())
+ auto camera = _mbglMap->getStyle().getDefaultCamera();
+ CGFloat pitch = *camera.pitch;
+ CLLocationDirection heading = mbgl::util::wrap(*camera.angle, 0., 360.);
+ CLLocationDistance distance = MGLAltitudeForZoomLevel(*camera.zoom, pitch, 0, self.frame.size);
+ self.camera = [MGLMapCamera cameraLookingAtCenterCoordinate:MGLLocationCoordinate2DFromLatLng(*camera.center)
fromDistance:distance
pitch:pitch
heading:heading];
@@ -2164,7 +2282,7 @@ public:
- (void)emptyMemoryCache
{
- _mbglMap->onLowMemory();
+ _rendererFrontend->onLowMemory();
}
- (void)setZoomEnabled:(BOOL)zoomEnabled
@@ -2198,8 +2316,61 @@ public:
- (NSString *)accessibilityValue
{
+ NSMutableArray *facts = [NSMutableArray array];
+
double zoomLevel = round(self.zoomLevel + 1);
- return [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE", nil, nil, @"Zoom %dx\n%ld annotation(s) visible", @"Map accessibility value"), (int)zoomLevel, (long)self.accessibilityAnnotationCount];
+ [facts addObject:[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE_ZOOM", nil, nil, @"Zoom %dx.", @"Map accessibility value; {zoom level}"), (int)zoomLevel]];
+
+ NSInteger annotationCount = self.accessibilityAnnotationCount;
+ if (annotationCount) {
+ [facts addObject:[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE_ANNOTATIONS", nil, nil, @"%ld annotation(s) visible.", @"Map accessibility value; {number of visible annotations}"), (long)self.accessibilityAnnotationCount]];
+ }
+
+ NSArray *placeFeatures = self.visiblePlaceFeatures;
+ if (placeFeatures.count) {
+ NSMutableArray *placesArray = [NSMutableArray arrayWithCapacity:placeFeatures.count];
+ NSMutableSet *placesSet = [NSMutableSet setWithCapacity:placeFeatures.count];
+ for (id <MGLFeature> placeFeature in placeFeatures.reverseObjectEnumerator) {
+ NSString *name = [placeFeature attributeForKey:@"name"];
+ if (![placesSet containsObject:name]) {
+ [placesArray addObject:name];
+ [placesSet addObject:name];
+ }
+ if (placesArray.count >= 3) {
+ break;
+ }
+ }
+ NSString *placesString = [placesArray componentsJoinedByString:NSLocalizedStringWithDefaultValue(@"LIST_SEPARATOR", nil, nil, @", ", @"List separator")];
+ [facts addObject:[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE_PLACES", nil, nil, @"Places visible: %@.", @"Map accessibility value; {list of visible places}"), placesString]];
+ }
+
+ NSArray *roadFeatures = self.visibleRoadFeatures;
+ if (roadFeatures.count) {
+ [facts addObject:[NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"MAP_A11Y_VALUE_ROADS", nil, nil, @"%ld road(s) visible.", @"Map accessibility value; {number of visible roads}"), roadFeatures.count]];
+ }
+
+ NSString *value = [facts componentsJoinedByString:@" "];
+ return value;
+}
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visiblePlaceFeatures
+{
+ if (!_visiblePlaceFeatures)
+ {
+ NSArray *placeStyleLayerIdentifiers = [self.style.placeStyleLayers valueForKey:@"identifier"];
+ _visiblePlaceFeatures = [self visibleFeaturesInRect:self.bounds inStyleLayersWithIdentifiers:[NSSet setWithArray:placeStyleLayerIdentifiers]];
+ }
+ return _visiblePlaceFeatures;
+}
+
+- (NS_ARRAY_OF(id <MGLFeature>) *)visibleRoadFeatures
+{
+ if (!_visibleRoadFeatures)
+ {
+ NSArray *roadStyleLayerIdentifiers = [self.style.roadStyleLayers valueForKey:@"identifier"];
+ _visibleRoadFeatures = [self visibleFeaturesInRect:self.bounds inStyleLayersWithIdentifiers:[NSSet setWithArray:roadStyleLayerIdentifiers]];
+ }
+ return _visibleRoadFeatures;
}
- (CGRect)accessibilityFrame
@@ -2233,14 +2404,9 @@ public:
{
if (self.calloutViewForSelectedAnnotation)
{
- return 2 /* selectedAnnotationCalloutView, mapViewProxyAccessibilityElement */;
+ return 2 /* calloutViewForSelectedAnnotation, mapViewProxyAccessibilityElement */;
}
- NSInteger count = self.accessibilityAnnotationCount + 2 /* compass, attributionButton */;
- if (self.userLocationAnnotationView)
- {
- count++;
- }
- return count;
+ return !!self.userLocationAnnotationView + self.accessibilityAnnotationCount + self.visiblePlaceFeatures.count + self.visibleRoadFeatures.count + 2 /* compass, attributionButton */;
}
- (NSInteger)accessibilityAnnotationCount
@@ -2265,67 +2431,123 @@ public:
}
return nil;
}
- std::vector<MGLAnnotationTag> visibleAnnotations = [self annotationTagsInRect:self.bounds];
-
- // Ornaments
- if (index == 0)
+
+ // Compass
+ NSUInteger compassIndex = 0;
+ if (index == compassIndex)
{
return self.compassView;
}
- if ( ! self.userLocationAnnotationView)
- {
- index++;
- }
- else if (index == 1)
+
+ // User location annotation
+ NSRange userLocationAnnotationRange = NSMakeRange(compassIndex + 1, !!self.userLocationAnnotationView);
+ if (NSLocationInRange(index, userLocationAnnotationRange))
{
return self.userLocationAnnotationView;
}
- if (index > 0 && (NSUInteger)index == visibleAnnotations.size() + 2 /* compass, userLocationAnnotationView */)
- {
- return self.attributionButton;
- }
-
- std::sort(visibleAnnotations.begin(), visibleAnnotations.end());
+
CGPoint centerPoint = self.contentCenter;
if (self.userTrackingMode != MGLUserTrackingModeNone)
{
centerPoint = self.userLocationAnnotationViewCenter;
}
- CLLocationCoordinate2D currentCoordinate = [self convertPoint:centerPoint toCoordinateFromView:self];
- std::sort(visibleAnnotations.begin(), visibleAnnotations.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;
- });
-
- NSUInteger annotationIndex = MGLAnnotationTagNotFound;
- if (index >= 0 && (NSUInteger)(index - 2) < visibleAnnotations.size())
+
+ // Visible annotations
+ std::vector<MGLAnnotationTag> visibleAnnotations = [self annotationTagsInRect:self.bounds];
+ NSRange visibleAnnotationRange = NSMakeRange(NSMaxRange(userLocationAnnotationRange), visibleAnnotations.size());
+ if (NSLocationInRange(index, visibleAnnotationRange))
+ {
+ std::sort(visibleAnnotations.begin(), visibleAnnotations.end());
+ std::sort(visibleAnnotations.begin(), visibleAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) {
+ CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate];
+ CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate];
+ CGPoint pointA = [self convertCoordinate:coordinateA toPointToView:self];
+ CGPoint pointB = [self convertCoordinate:coordinateB toPointToView:self];
+ CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y);
+ CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y);
+ return deltaA < deltaB;
+ });
+
+ NSUInteger annotationIndex = index - visibleAnnotationRange.location;
+ MGLAnnotationTag annotationTag = visibleAnnotations[annotationIndex];
+ NSAssert(annotationTag != MGLAnnotationTagNotFound, @"Can’t get accessibility element for nonexistent or invisible annotation at index %li.", (long)index);
+ return [self accessibilityElementForAnnotationWithTag:annotationTag];
+ }
+
+ // Visible place features
+ NSArray *visiblePlaceFeatures = self.visiblePlaceFeatures;
+ NSRange visiblePlaceFeatureRange = NSMakeRange(NSMaxRange(visibleAnnotationRange), visiblePlaceFeatures.count);
+ if (NSLocationInRange(index, visiblePlaceFeatureRange))
+ {
+ visiblePlaceFeatures = [visiblePlaceFeatures sortedArrayUsingComparator:^NSComparisonResult(id <MGLFeature> _Nonnull featureA, id <MGLFeature> _Nonnull featureB) {
+ CGPoint pointA = [self convertCoordinate:featureA.coordinate toPointToView:self];
+ CGPoint pointB = [self convertCoordinate:featureB.coordinate toPointToView:self];
+ CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y);
+ CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y);
+ return [@(deltaA) compare:@(deltaB)];
+ }];
+
+ id <MGLFeature> feature = visiblePlaceFeatures[index - visiblePlaceFeatureRange.location];
+ return [self accessibilityElementForPlaceFeature:feature];
+ }
+
+ // Visible road features
+ NSArray *visibleRoadFeatures = self.visibleRoadFeatures;
+ NSRange visibleRoadFeatureRange = NSMakeRange(NSMaxRange(visiblePlaceFeatureRange), visibleRoadFeatures.count);
+ if (NSLocationInRange(index, visibleRoadFeatureRange))
+ {
+ visibleRoadFeatures = [visibleRoadFeatures sortedArrayUsingComparator:^NSComparisonResult(id <MGLFeature> _Nonnull featureA, id <MGLFeature> _Nonnull featureB) {
+ CGPoint pointA = [self convertCoordinate:featureA.coordinate toPointToView:self];
+ CGPoint pointB = [self convertCoordinate:featureB.coordinate toPointToView:self];
+ CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y);
+ CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y);
+ return [@(deltaA) compare:@(deltaB)];
+ }];
+
+ id <MGLFeature> feature = visibleRoadFeatures[index - visibleRoadFeatureRange.location];
+ return [self accessibilityElementForRoadFeature:feature];
+ }
+
+ // Attribution button
+ NSUInteger attributionButtonIndex = NSMaxRange(visibleRoadFeatureRange);
+ if (index == attributionButtonIndex)
{
- annotationIndex = index - 2 /* compass, userLocationAnnotationView */;
+ return self.attributionButton;
}
- MGLAnnotationTag annotationTag = visibleAnnotations[annotationIndex];
- NSAssert(annotationTag != MGLAnnotationTagNotFound, @"Can’t get accessibility element for nonexistent or invisible annotation at index %li.", (long)index);
+
+ NSAssert(NO, @"Index %ld not in recognized accessibility element ranges. "
+ @"User location annotation range: %@; visible annotation range: %@; "
+ @"visible place feature range: %@; visible road feature range: %@.",
+ (long)index, NSStringFromRange(userLocationAnnotationRange),
+ NSStringFromRange(visibleAnnotationRange), NSStringFromRange(visiblePlaceFeatureRange),
+ NSStringFromRange(visibleRoadFeatureRange));
+ return nil;
+}
+
+/**
+ Returns an accessibility element corresponding to a visible annotation with the given tag.
+
+ @param annotationTag Tag of the annotation represented by the accessibility element to return.
+ */
+- (id)accessibilityElementForAnnotationWithTag:(MGLAnnotationTag)annotationTag
+{
NSAssert(_annotationContextsByAnnotationTag.count(annotationTag), @"Missing annotation for tag %u.", annotationTag);
MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
id <MGLAnnotation> annotation = annotationContext.annotation;
-
+
// Let the annotation view serve as its own accessibility element.
MGLAnnotationView *annotationView = annotationContext.annotationView;
if (annotationView && annotationView.superview)
{
return annotationView;
}
-
+
// Lazily create an accessibility element for the found annotation.
if ( ! annotationContext.accessibilityElement)
{
annotationContext.accessibilityElement = [[MGLAnnotationAccessibilityElement alloc] initWithAccessibilityContainer:self tag:annotationTag];
}
-
+
// Update the accessibility element.
MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
CGRect annotationFrame = [self frameOfImage:annotationImage.image centeredAtCoordinate:annotation.coordinate];
@@ -2336,8 +2558,7 @@ public:
annotationFrame = CGRectUnion(annotationFrame, minimumFrame);
CGRect screenRect = UIAccessibilityConvertFrameToScreenCoordinates(annotationFrame, self);
annotationContext.accessibilityElement.accessibilityFrame = screenRect;
- annotationContext.accessibilityElement.accessibilityHint = NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint");
-
+
if ([annotation respondsToSelector:@selector(title)])
{
annotationContext.accessibilityElement.accessibilityLabel = annotation.title;
@@ -2346,10 +2567,114 @@ public:
{
annotationContext.accessibilityElement.accessibilityValue = annotation.subtitle;
}
-
+
return annotationContext.accessibilityElement;
}
+/**
+ Returns an accessibility element corresponding to the given place feature.
+
+ @param feature The place feature represented by the accessibility element.
+ */
+- (id)accessibilityElementForPlaceFeature:(id <MGLFeature>)feature
+{
+ if (!_featureAccessibilityElements)
+ {
+ _featureAccessibilityElements = [NSMutableSet set];
+ }
+
+ MGLFeatureAccessibilityElement *element = [_featureAccessibilityElements objectsPassingTest:^BOOL(MGLFeatureAccessibilityElement * _Nonnull element, BOOL * _Nonnull stop) {
+ return element.feature.identifier && ![element.feature.identifier isEqual:@0] && [element.feature.identifier isEqual:feature.identifier];
+ }].anyObject;
+ if (!element)
+ {
+ element = [[MGLPlaceFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
+ }
+ CGPoint center = [self convertCoordinate:feature.coordinate toPointToView:self];
+ CGRect annotationFrame = CGRectInset({center, CGSizeZero}, -MGLAnnotationAccessibilityElementMinimumSize.width / 2, -MGLAnnotationAccessibilityElementMinimumSize.width / 2);
+ CGRect screenRect = UIAccessibilityConvertFrameToScreenCoordinates(annotationFrame, self);
+ element.accessibilityFrame = screenRect;
+
+ [_featureAccessibilityElements addObject:element];
+
+ return element;
+}
+
+/**
+ Returns an accessibility element corresponding to the given road feature.
+
+ @param feature The road feature represented by the accessibility element.
+ */
+- (id)accessibilityElementForRoadFeature:(id <MGLFeature>)feature
+{
+ if (!_featureAccessibilityElements)
+ {
+ _featureAccessibilityElements = [NSMutableSet set];
+ }
+
+ MGLFeatureAccessibilityElement *element = [_featureAccessibilityElements objectsPassingTest:^BOOL(MGLFeatureAccessibilityElement * _Nonnull element, BOOL * _Nonnull stop) {
+ return element.feature.identifier && ![element.feature.identifier isEqual:@0] && [element.feature.identifier isEqual:feature.identifier];
+ }].anyObject;
+ if (!element)
+ {
+ element = [[MGLRoadFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
+ }
+
+ UIBezierPath *path;
+ if ([feature isKindOfClass:[MGLPointFeature class]])
+ {
+ CGPoint center = [self convertCoordinate:feature.coordinate toPointToView:self];
+ CGRect annotationFrame = CGRectInset({center, CGSizeZero}, -MGLAnnotationAccessibilityElementMinimumSize.width / 2, -MGLAnnotationAccessibilityElementMinimumSize.width / 2);
+ CGRect screenRect = UIAccessibilityConvertFrameToScreenCoordinates(annotationFrame, self);
+ element.accessibilityFrame = screenRect;
+ }
+ else if ([feature isKindOfClass:[MGLPolylineFeature class]])
+ {
+ path = [self pathOfPolyline:(MGLPolyline *)feature];
+ }
+ else if ([feature isKindOfClass:[MGLMultiPolylineFeature class]])
+ {
+ path = [UIBezierPath bezierPath];
+ for (MGLPolyline *polyline in [(MGLMultiPolylineFeature *)feature polylines])
+ {
+ [path appendPath:[self pathOfPolyline:polyline]];
+ }
+ }
+
+ if (path)
+ {
+ CGPathRef strokedCGPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, MGLAnnotationAccessibilityElementMinimumSize.width, kCGLineCapButt, kCGLineJoinMiter, 0);
+ UIBezierPath *strokedPath = [UIBezierPath bezierPathWithCGPath:strokedCGPath];
+ CGPathRelease(strokedCGPath);
+ UIBezierPath *screenPath = UIAccessibilityConvertPathToScreenCoordinates(strokedPath, self);
+ element.accessibilityPath = screenPath;
+ }
+
+ [_featureAccessibilityElements addObject:element];
+
+ return element;
+}
+
+- (UIBezierPath *)pathOfPolyline:(MGLPolyline *)polyline
+{
+ CLLocationCoordinate2D *coordinates = polyline.coordinates;
+ NSUInteger pointCount = polyline.pointCount;
+ UIBezierPath *path = [UIBezierPath bezierPath];
+ for (NSUInteger i = 0; i < pointCount; i++)
+ {
+ CGPoint point = [self convertCoordinate:coordinates[i] toPointToView:self];
+ if (i)
+ {
+ [path addLineToPoint:point];
+ }
+ else
+ {
+ [path moveToPoint:point];
+ }
+ }
+ return path;
+}
+
- (NSInteger)indexOfAccessibilityElement:(id)element
{
if (self.calloutViewForSelectedAnnotation)
@@ -2357,17 +2682,30 @@ public:
return [@[self.calloutViewForSelectedAnnotation, self.mapViewProxyAccessibilityElement]
indexOfObject:element];
}
+
+ // Compass
+ NSUInteger compassIndex = 0;
if (element == self.compassView)
{
- return 0;
+ return compassIndex;
}
+
+ // User location annotation
+ NSRange userLocationAnnotationRange = NSMakeRange(compassIndex + 1, !!self.userLocationAnnotationView);
if (element == self.userLocationAnnotationView)
{
- return 1;
+ return userLocationAnnotationRange.location;
}
-
+
+ CGPoint centerPoint = self.contentCenter;
+ if (self.userTrackingMode != MGLUserTrackingModeNone)
+ {
+ centerPoint = self.userLocationAnnotationViewCenter;
+ }
+
+ // Visible annotations
std::vector<MGLAnnotationTag> visibleAnnotations = [self annotationTagsInRect:self.bounds];
-
+ NSRange visibleAnnotationRange = NSMakeRange(NSMaxRange(userLocationAnnotationRange), visibleAnnotations.size());
MGLAnnotationTag tag = MGLAnnotationTagNotFound;
if ([element isKindOfClass:[MGLAnnotationView class]])
{
@@ -2378,22 +2716,92 @@ public:
{
tag = [(MGLAnnotationAccessibilityElement *)element tag];
}
- else if (element == self.attributionButton)
- {
- return !!self.userLocationAnnotationView + visibleAnnotations.size();
+
+ if (tag != MGLAnnotationTagNotFound)
+ {
+ std::sort(visibleAnnotations.begin(), visibleAnnotations.end());
+ std::sort(visibleAnnotations.begin(), visibleAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) {
+ CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate];
+ CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate];
+ CGPoint pointA = [self convertCoordinate:coordinateA toPointToView:self];
+ CGPoint pointB = [self convertCoordinate:coordinateB toPointToView:self];
+ CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y);
+ CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y);
+ return deltaA < deltaB;
+ });
+
+ auto foundElement = std::find(visibleAnnotations.begin(), visibleAnnotations.end(), tag);
+ if (foundElement == visibleAnnotations.end())
+ {
+ return NSNotFound;
+ }
+ return visibleAnnotationRange.location + std::distance(visibleAnnotations.begin(), foundElement);
}
- else
- {
- return NSNotFound;
+
+ // Visible place features
+ NSArray *visiblePlaceFeatures = self.visiblePlaceFeatures;
+ NSRange visiblePlaceFeatureRange = NSMakeRange(NSMaxRange(visibleAnnotationRange), visiblePlaceFeatures.count);
+ if ([element isKindOfClass:[MGLPlaceFeatureAccessibilityElement class]])
+ {
+ visiblePlaceFeatures = [visiblePlaceFeatures sortedArrayUsingComparator:^NSComparisonResult(id <MGLFeature> _Nonnull featureA, id <MGLFeature> _Nonnull featureB) {
+ CGPoint pointA = [self convertCoordinate:featureA.coordinate toPointToView:self];
+ CGPoint pointB = [self convertCoordinate:featureB.coordinate toPointToView:self];
+ CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y);
+ CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y);
+ return [@(deltaA) compare:@(deltaB)];
+ }];
+
+ id <MGLFeature> feature = [(MGLPlaceFeatureAccessibilityElement *)element feature];
+ NSUInteger featureIndex = [visiblePlaceFeatures indexOfObject:feature];
+ if (featureIndex == NSNotFound)
+ {
+ featureIndex = [visiblePlaceFeatures indexOfObjectPassingTest:^BOOL (id <MGLFeature> _Nonnull visibleFeature, NSUInteger idx, BOOL * _Nonnull stop) {
+ return visibleFeature.identifier && ![visibleFeature.identifier isEqual:@0] && [visibleFeature.identifier isEqual:feature.identifier];
+ }];
+ }
+ if (featureIndex == NSNotFound)
+ {
+ return NSNotFound;
+ }
+ return visiblePlaceFeatureRange.location + featureIndex;
}
-
- std::sort(visibleAnnotations.begin(), visibleAnnotations.end());
- auto foundElement = std::find(visibleAnnotations.begin(), visibleAnnotations.end(), tag);
- if (foundElement == visibleAnnotations.end())
+
+ // Visible road features
+ NSArray *visibleRoadFeatures = self.visibleRoadFeatures;
+ NSRange visibleRoadFeatureRange = NSMakeRange(NSMaxRange(visiblePlaceFeatureRange), visibleRoadFeatures.count);
+ if ([element isKindOfClass:[MGLRoadFeatureAccessibilityElement class]])
+ {
+ visibleRoadFeatures = [visibleRoadFeatures sortedArrayUsingComparator:^NSComparisonResult(id <MGLFeature> _Nonnull featureA, id <MGLFeature> _Nonnull featureB) {
+ CGPoint pointA = [self convertCoordinate:featureA.coordinate toPointToView:self];
+ CGPoint pointB = [self convertCoordinate:featureB.coordinate toPointToView:self];
+ CGFloat deltaA = hypot(pointA.x - centerPoint.x, pointA.y - centerPoint.y);
+ CGFloat deltaB = hypot(pointB.x - centerPoint.x, pointB.y - centerPoint.y);
+ return [@(deltaA) compare:@(deltaB)];
+ }];
+
+ id <MGLFeature> feature = [(MGLRoadFeatureAccessibilityElement *)element feature];
+ NSUInteger featureIndex = [visibleRoadFeatures indexOfObject:feature];
+ if (featureIndex == NSNotFound)
+ {
+ featureIndex = [visibleRoadFeatures indexOfObjectPassingTest:^BOOL (id <MGLFeature> _Nonnull visibleFeature, NSUInteger idx, BOOL * _Nonnull stop) {
+ return visibleFeature.identifier && ![visibleFeature.identifier isEqual:@0] && [visibleFeature.identifier isEqual:feature.identifier];
+ }];
+ }
+ if (featureIndex == NSNotFound)
+ {
+ return NSNotFound;
+ }
+ return visibleRoadFeatureRange.location + featureIndex;
+ }
+
+ // Attribution button
+ NSUInteger attributionButtonIndex = NSMaxRange(visibleRoadFeatureRange);
+ if (element == self.attributionButton)
{
- return NSNotFound;
+ return attributionButtonIndex;
}
- return !!self.userLocationAnnotationView + std::distance(visibleAnnotations.begin(), foundElement) + 1 /* compass */;
+
+ return NSNotFound;
}
- (MGLMapViewProxyAccessibilityElement *)mapViewProxyAccessibilityElement
@@ -2424,10 +2832,11 @@ public:
{
centerPoint = self.userLocationAnnotationViewCenter;
}
- _mbglMap->setZoom(_mbglMap->getZoom() + log2(scaleFactor), mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ double newZoom = round(self.zoomLevel) + log2(scaleFactor);
+ _mbglMap->setZoom(newZoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
[self unrotateIfNeededForGesture];
- UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.accessibilityValue);
+ _accessibilityValueAnnouncementIsPending = YES;
}
#pragma mark - Geography -
@@ -2547,8 +2956,6 @@ public:
- (void)setMinimumZoomLevel:(double)minimumZoomLevel
{
- _mbglMap->setMinZoom(minimumZoomLevel);
- [self validateTileCacheSize];
}
- (double)minimumZoomLevel
@@ -2559,7 +2966,6 @@ public:
- (void)setMaximumZoomLevel:(double)maximumZoomLevel
{
_mbglMap->setMaxZoom(maximumZoomLevel);
- [self validateTileCacheSize];
}
- (double)maximumZoomLevel
@@ -2762,6 +3168,10 @@ public:
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion
{
+ [self setCamera:camera withDuration:duration animationTimingFunction:function edgePadding:self.contentInset completionHandler:completion];
+}
+
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(UIEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion {
mbgl::AnimationOptions animationOptions;
if (duration > 0)
{
@@ -2790,7 +3200,7 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
- mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:self.contentInset];
+ mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding];
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
}
@@ -2864,6 +3274,16 @@ public:
return [self cameraForCameraOptions:cameraOptions];
}
+- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(UIEdgeInsets)insets {
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
+ padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset);
+
+ mbgl::CameraOptions cameraOptions = _mbglMap->cameraForGeometry([shape geometryObject], padding, direction);
+
+ return [self cameraForCameraOptions:cameraOptions];
+
+}
+
- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions
{
CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng());
@@ -3099,6 +3519,12 @@ public:
}
std::vector<MGLAnnotationTag> annotationTags = [self annotationTagsInRect:rect];
+ std::vector<MGLAnnotationTag> shapeAnnotationTags = [self shapeAnnotationTagsInRect:rect];
+
+ if (shapeAnnotationTags.size()) {
+ annotationTags.insert(annotationTags.end(), shapeAnnotationTags.begin(), shapeAnnotationTags.end());
+ }
+
if (annotationTags.size())
{
NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()];
@@ -3215,7 +3641,7 @@ public:
{
annotationViewsForAnnotation[annotationValue] = annotationView;
annotationView.annotation = annotation;
- annotationView.center = [self convertCoordinate:annotation.coordinate toPointToView:self];
+ annotationView.center = MGLPointRounded([self convertCoordinate:annotation.coordinate toPointToView:self]);
[newAnnotationViews addObject:annotationView];
MGLAnnotationImage *annotationImage = self.invisibleAnnotationImage;
@@ -3604,6 +4030,11 @@ public:
queryRect = CGRectInset(queryRect, -MGLAnnotationImagePaddingForHitTest,
-MGLAnnotationImagePaddingForHitTest);
std::vector<MGLAnnotationTag> nearbyAnnotations = [self annotationTagsInRect:queryRect];
+ std::vector<MGLAnnotationTag> nearbyShapeAnnotations = [self shapeAnnotationTagsInRect:queryRect];
+
+ if (nearbyShapeAnnotations.size()) {
+ nearbyAnnotations.insert(nearbyAnnotations.end(), nearbyShapeAnnotations.begin(), nearbyShapeAnnotations.end());
+ }
if (nearbyAnnotations.size())
{
@@ -3611,54 +4042,59 @@ public:
CGRect hitRect = CGRectInset({ point, CGSizeZero },
-MGLAnnotationImagePaddingForHitTest,
-MGLAnnotationImagePaddingForHitTest);
-
+
// Filter out any annotation whose image or view is unselectable or for which
// hit testing fails.
- auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(),
- [&](const MGLAnnotationTag annotationTag)
- {
+ auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag annotationTag) {
id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag];
NSAssert(annotation, @"Unknown annotation found nearby tap");
if ( ! annotation)
{
return true;
}
-
+
MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
CGRect annotationRect;
-
+
MGLAnnotationView *annotationView = annotationContext.annotationView;
+
if (annotationView)
{
if ( ! annotationView.enabled)
{
return true;
}
-
- CGPoint calloutAnchorPoint = [self convertCoordinate:annotation.coordinate toPointToView:self];
+
+ CGPoint calloutAnchorPoint = MGLPointRounded([self convertCoordinate:annotation.coordinate toPointToView:self]);
CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -CGRectGetWidth(annotationView.frame) / 2, -CGRectGetHeight(annotationView.frame) / 2);
annotationRect = UIEdgeInsetsInsetRect(frame, annotationView.alignmentRectInsets);
}
else
{
+ if ([annotation isKindOfClass:[MGLShape class]])
+ {
+ return false;
+ }
+
MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
if ( ! annotationImage.enabled)
{
return true;
}
-
+
MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName];
UIImage *fallbackImage = fallbackAnnotationImage.image;
-
+
annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate];
}
-
+
// Filter out the annotation if the fattened finger didn’t land
// within the image’s alignment rect.
return !!!CGRectIntersectsRect(annotationRect, hitRect);
});
-
+
nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end));
+
}
MGLAnnotationTag hitAnnotationTag = MGLAnnotationTagNotFound;
@@ -3735,7 +4171,15 @@ public:
/// Returns the tags of the annotations coincident with the given rectangle.
- (std::vector<MGLAnnotationTag>)annotationTagsInRect:(CGRect)rect
{
- return _mbglMap->queryPointAnnotations({
+ return _rendererFrontend->getRenderer()->queryPointAnnotations({
+ { CGRectGetMinX(rect), CGRectGetMinY(rect) },
+ { CGRectGetMaxX(rect), CGRectGetMaxY(rect) },
+ });
+}
+
+- (std::vector<MGLAnnotationTag>)shapeAnnotationTagsInRect:(CGRect)rect
+{
+ return _rendererFrontend->getRenderer()->queryShapeAnnotations({
{ CGRectGetMinX(rect), CGRectGetMinY(rect) },
{ CGRectGetMaxX(rect), CGRectGetMaxY(rect) },
});
@@ -3790,17 +4234,16 @@ public:
- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated
{
- if ( ! annotation) return;
+ CGRect positioningRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:CGPointZero];
+ [self selectAnnotation:annotation animated:animated calloutPositioningRect:positioningRect];
+}
- if ([annotation isKindOfClass:[MGLMultiPoint class]]) return;
+- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated calloutPositioningRect:(CGRect)calloutPositioningRect
+{
+ if ( ! annotation) return;
if (annotation == self.selectedAnnotation) return;
- if (annotation != self.userLocation)
- {
- self.userTrackingMode = MGLUserTrackingModeNone;
- }
-
[self deselectAnnotation:self.selectedAnnotation animated:NO];
// Add the annotation to the map if it hasn’t been added yet.
@@ -3812,9 +4255,6 @@ public:
if (annotationTag == MGLAnnotationTagNotFound) return;
}
- // By default attempt to use the GL annotation image frame as the positioning rect.
- CGRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag];
-
MGLAnnotationView *annotationView = nil;
if (annotation != self.userLocation)
@@ -3822,21 +4262,12 @@ 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.
- positioningRect = 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:animated];
}
}
- }
-
- // The client can request that any annotation be selected (even ones that are offscreen).
- // The annotation can’t be selected if no part of it is hittable.
- if ( ! CGRectIntersectsRect(positioningRect, self.bounds) && annotation != self.userLocation)
- {
- return;
- }
self.selectedAnnotation = annotation;
@@ -3866,7 +4297,7 @@ public:
if (_userLocationAnnotationIsSelected)
{
- positioningRect = [self.userLocationAnnotationView.layer.presentationLayer frame];
+ calloutPositioningRect = [self.userLocationAnnotationView.layer.presentationLayer frame];
CGRect implicitAnnotationFrame = [self.userLocationAnnotationView.layer.presentationLayer frame];
CGRect explicitAnnotationFrame = self.userLocationAnnotationView.frame;
@@ -3882,7 +4313,7 @@ public:
if ([calloutView.leftAccessoryView isKindOfClass:[UIControl class]])
{
UITapGestureRecognizer *calloutAccessoryTap = [[UITapGestureRecognizer alloc] initWithTarget:self
- action:@selector(handleCalloutAccessoryTapGesture:)];
+ action:@selector(handleCalloutAccessoryTapGesture:)];
[calloutView.leftAccessoryView addGestureRecognizer:calloutAccessoryTap];
}
@@ -3895,7 +4326,7 @@ public:
if ([calloutView.rightAccessoryView isKindOfClass:[UIControl class]])
{
UITapGestureRecognizer *calloutAccessoryTap = [[UITapGestureRecognizer alloc] initWithTarget:self
- action:@selector(handleCalloutAccessoryTapGesture:)];
+ action:@selector(handleCalloutAccessoryTapGesture:)];
[calloutView.rightAccessoryView addGestureRecognizer:calloutAccessoryTap];
}
@@ -3905,7 +4336,7 @@ public:
calloutView.delegate = self;
// present popup
- [calloutView presentCalloutFromRect:positioningRect
+ [calloutView presentCalloutFromRect:calloutPositioningRect
inView:self.glView
constrainedToView:self.glView
animated:animated];
@@ -3935,6 +4366,27 @@ public:
/// Returns the rectangle that represents the annotation image of the annotation
/// with the given tag. This rectangle is fitted to the image’s alignment rect
/// and is appropriate for positioning a popover.
+/// If a shape annotation is visible but its centroid is not, and a default point is specified,
+/// the callout view is anchored to the default callout point.
+- (CGRect)positioningRectForAnnotation:(id <MGLAnnotation>)annotation defaultCalloutPoint:(CGPoint)calloutPoint
+{
+ MGLAnnotationTag annotationTag = [self annotationTagForAnnotation:annotation];
+ CGRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag];
+
+ // For annotations which `coordinate` falls offscreen it will use the current tap point as anchor instead.
+ if ( ! CGRectIntersectsRect(positioningRect, self.bounds) && annotation != self.userLocation)
+ {
+ if (!CGPointEqualToPoint(calloutPoint, CGPointZero)) {
+ positioningRect = CGRectMake(calloutPoint.x, calloutPoint.y, positioningRect.size.width, positioningRect.size.height);
+ }
+ }
+
+ return positioningRect;
+}
+
+/// Returns the rectangle that represents the annotation image of the annotation
+/// with the given tag. This rectangle is fitted to the image’s alignment rect
+/// and is appropriate for positioning a popover.
- (CGRect)positioningRectForCalloutForAnnotationWithTag:(MGLAnnotationTag)annotationTag
{
id <MGLAnnotation> annotation = [self annotationWithTag:annotationTag];
@@ -3942,6 +4394,13 @@ public:
{
return CGRectZero;
}
+
+ if ([annotation isKindOfClass:[MGLMultiPoint class]]) {
+ CLLocationCoordinate2D origin = annotation.coordinate;
+ CGPoint originPoint = [self convertCoordinate:origin toPointToView:self];
+ return CGRectMake(originPoint.x, originPoint.y, MGLAnnotationImagePaddingForHitTest, MGLAnnotationImagePaddingForHitTest);
+
+ }
UIImage *image = [self imageOfAnnotationWithTag:annotationTag].image;
if ( ! image)
{
@@ -3963,7 +4422,7 @@ public:
/// image centered at the given coordinate.
- (CGRect)frameOfImage:(UIImage *)image centeredAtCoordinate:(CLLocationCoordinate2D)coordinate
{
- CGPoint calloutAnchorPoint = [self convertCoordinate:coordinate toPointToView:self];
+ CGPoint calloutAnchorPoint = MGLPointRounded([self convertCoordinate:coordinate toPointToView:self]);
CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -image.size.width / 2, -image.size.height / 2);
return UIEdgeInsetsInsetRect(frame, image.alignmentRectInsets);
}
@@ -4146,33 +4605,41 @@ public:
{
self.locationManager = [[CLLocationManager alloc] init];
- if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)] && [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)
+ if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)
{
- BOOL hasLocationDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] || [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"];
- if (!hasLocationDescription)
+ BOOL requiresWhenInUseUsageDescription = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){11,0,0}];
+ BOOL hasWhenInUseUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"];
+ BOOL hasAlwaysUsageDescription;
+ if (requiresWhenInUseUsageDescription)
{
- [NSException raise:@"Missing Location Services usage description" format:
- @"This app must have a value for NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription in its Info.plist."];
+ hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"] && hasWhenInUseUsageDescription;
+ }
+ else
+ {
+ hasAlwaysUsageDescription = !![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"];
}
- if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"])
+ if (hasAlwaysUsageDescription)
{
[self.locationManager requestAlwaysAuthorization];
}
- else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"])
+ else if (hasWhenInUseUsageDescription)
{
[self.locationManager requestWhenInUseAuthorization];
}
+ else
+ {
+ NSString *suggestedUsageKeys = requiresWhenInUseUsageDescription ?
+ @"NSLocationWhenInUseUsageDescription and (optionally) NSLocationAlwaysAndWhenInUseUsageDescription" :
+ @"NSLocationWhenInUseUsageDescription and/or NSLocationAlwaysUsageDescription";
+ [NSException raise:@"Missing Location Services usage description" format:@"This app must have a value for %@ in its Info.plist.", suggestedUsageKeys];
+ }
}
- self.locationManager.headingFilter = 5.0;
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
- if (self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)
- {
- [self.locationManager startUpdatingHeading];
- }
+ [self validateUserHeadingUpdating];
}
else if ( ! shouldEnableLocationServices && self.locationManager)
{
@@ -4296,8 +4763,6 @@ public:
{
self.userTrackingState = MGLUserTrackingStatePossible;
- [self.locationManager stopUpdatingHeading];
-
// Immediately update the annotation view; other cases update inside
// the locationManager:didUpdateLocations: method.
[self updateUserLocationAnnotationView];
@@ -4310,14 +4775,6 @@ public:
self.userTrackingState = animated ? MGLUserTrackingStatePossible : MGLUserTrackingStateChanged;
self.showsUserLocation = YES;
- [self.locationManager stopUpdatingHeading];
-
- CLLocation *location = self.userLocation.location;
- if (location && self.userLocationAnnotationView)
- {
- [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated];
- }
-
break;
}
case MGLUserTrackingModeFollowWithHeading:
@@ -4334,19 +4791,21 @@ public:
[self setZoomLevel:self.currentMinimumZoom animated:YES];
}
- if (self.userLocationAnnotationView)
- {
- [self locationManager:self.locationManager didUpdateLocations:@[self.userLocation.location] animated:animated];
- }
-
- [self updateHeadingForDeviceOrientation];
-
- [self.locationManager startUpdatingHeading];
-
break;
}
}
+ if (_userTrackingMode != MGLUserTrackingModeNone)
+ {
+ CLLocation *location = self.userLocation.location;
+ if (location && self.userLocationAnnotationView)
+ {
+ [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated];
+ }
+ }
+
+ [self validateUserHeadingUpdating];
+
if ([self.delegate respondsToSelector:@selector(mapView:didChangeUserTrackingMode:animated:)])
{
[self.delegate mapView:self didChangeUserTrackingMode:_userTrackingMode animated:animated];
@@ -4385,14 +4844,42 @@ public:
if (self.userTrackingMode == MGLUserTrackingModeFollowWithCourse)
{
self.userTrackingState = MGLUserTrackingStatePossible;
- if (self.userLocation.location)
+
+ CLLocation *location = self.userLocation.location;
+ if (location)
{
- [self locationManager:self.locationManager didUpdateLocations:@[self.userLocation.location] animated:animated];
+ [self locationManager:self.locationManager didUpdateLocations:@[location] animated:animated];
}
}
}
}
+- (void)setShowsUserHeadingIndicator:(BOOL)showsUserHeadingIndicator
+{
+ _showsUserHeadingIndicator = showsUserHeadingIndicator;
+
+ if (_showsUserHeadingIndicator)
+ {
+ self.showsUserLocation = YES;
+ }
+ [self validateUserHeadingUpdating];
+}
+
+- (void)validateUserHeadingUpdating
+{
+ BOOL canShowPermanentHeadingIndicator = self.showsUserHeadingIndicator && self.userTrackingMode != MGLUserTrackingModeFollowWithCourse;
+
+ if (canShowPermanentHeadingIndicator || self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)
+ {
+ [self updateHeadingForDeviceOrientation];
+ [self.locationManager startUpdatingHeading];
+ }
+ else
+ {
+ [self.locationManager stopUpdatingHeading];
+ }
+}
+
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
[self locationManager:manager didUpdateLocations:locations animated:YES];
@@ -4402,6 +4889,7 @@ public:
{
CLLocation *oldLocation = self.userLocation.location;
CLLocation *newLocation = locations.lastObject;
+ _distanceFromOldUserLocation = [newLocation distanceFromLocation:oldLocation];
if ( ! _showsUserLocation || ! newLocation || ! CLLocationCoordinate2DIsValid(newLocation.coordinate)) return;
@@ -4495,7 +4983,12 @@ public:
/// first location update.
- (void)didUpdateLocationSignificantlyAnimated:(BOOL)animated
{
- self.userTrackingState = MGLUserTrackingStateBegan;
+
+ if (_distanceFromOldUserLocation >= MGLDistanceThresholdForCameraPause) {
+ self.userTrackingState = MGLUserTrackingStateBeginSignificantTransition;
+ } else {
+ self.userTrackingState = MGLUserTrackingStateBegan;
+ }
MGLMapCamera *camera = self.camera;
camera.centerCoordinate = self.userLocation.location.coordinate;
@@ -4515,7 +5008,8 @@ public:
peakAltitude:-1
completionHandler:^{
MGLMapView *strongSelf = weakSelf;
- if (strongSelf.userTrackingState == MGLUserTrackingStateBegan)
+ if (strongSelf.userTrackingState == MGLUserTrackingStateBegan ||
+ strongSelf.userTrackingState == MGLDistanceThresholdForCameraPause)
{
strongSelf.userTrackingState = MGLUserTrackingStateChanged;
}
@@ -4633,6 +5127,11 @@ public:
self.userLocation.heading = newHeading;
+ if (self.showsUserHeadingIndicator || self.userTrackingMode == MGLUserTrackingModeFollowWithHeading)
+ {
+ [self updateUserLocationAnnotationView];
+ }
+
if ([self.delegate respondsToSelector:@selector(mapView:didUpdateUserLocation:)])
{
[self.delegate mapView:self didUpdateUserLocation:self.userLocation];
@@ -4669,30 +5168,39 @@ public:
{
// note that right/left device and interface orientations are opposites (see UIApplication.h)
//
+ CLDeviceOrientation orientation;
switch ([[UIApplication sharedApplication] statusBarOrientation])
{
case (UIInterfaceOrientationLandscapeLeft):
{
- self.locationManager.headingOrientation = CLDeviceOrientationLandscapeRight;
+ orientation = CLDeviceOrientationLandscapeRight;
break;
}
case (UIInterfaceOrientationLandscapeRight):
{
- self.locationManager.headingOrientation = CLDeviceOrientationLandscapeLeft;
+ orientation = CLDeviceOrientationLandscapeLeft;
break;
}
case (UIInterfaceOrientationPortraitUpsideDown):
{
- self.locationManager.headingOrientation = CLDeviceOrientationPortraitUpsideDown;
+ orientation = CLDeviceOrientationPortraitUpsideDown;
break;
}
case (UIInterfaceOrientationPortrait):
default:
{
- self.locationManager.headingOrientation = CLDeviceOrientationPortrait;
+ orientation = CLDeviceOrientationPortrait;
break;
}
}
+
+ // Setting the location manager's heading orientation causes it to send
+ // a heading event, which in turn makes us redraw, which kicks off a
+ // loop... so don't do that. rdar://34059173
+ if (self.locationManager.headingOrientation != orientation)
+ {
+ self.locationManager.headingOrientation = orientation;
+ }
}
}
@@ -4728,7 +5236,7 @@ public:
optionalFilter = predicate.mgl_filter;
}
- std::vector<mbgl::Feature> features = _mbglMap->queryRenderedFeatures(screenCoordinate, { optionalLayerIDs, optionalFilter });
+ std::vector<mbgl::Feature> features = _rendererFrontend->getRenderer()->queryRenderedFeatures(screenCoordinate, { optionalLayerIDs, optionalFilter });
return MGLFeaturesFromMBGLFeatures(features);
}
@@ -4761,7 +5269,7 @@ public:
optionalFilter = predicate.mgl_filter;
}
- std::vector<mbgl::Feature> features = _mbglMap->queryRenderedFeatures(screenBox, { optionalLayerIDs, optionalFilter });
+ std::vector<mbgl::Feature> features = _rendererFrontend->getRenderer()->queryRenderedFeatures(screenBox, { optionalLayerIDs, optionalFilter });
return MGLFeaturesFromMBGLFeatures(features);
}
@@ -4888,12 +5396,26 @@ public:
{
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
- UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
+ _featureAccessibilityElements = nil;
+ _visiblePlaceFeatures = nil;
+ _visibleRoadFeatures = nil;
+ if (_accessibilityValueAnnouncementIsPending) {
+ _accessibilityValueAnnouncementIsPending = NO;
+ [self performSelector:@selector(announceAccessibilityValue) withObject:nil afterDelay:0.1];
+ } else {
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
+ }
}
[self.delegate mapView:self regionDidChangeAnimated:animated];
}
}
+- (void)announceAccessibilityValue
+{
+ UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.accessibilityValue);
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
+}
+
- (void)mapViewWillStartLoadingMap {
if (!_mbglMap) {
return;
@@ -4975,6 +5497,8 @@ public:
if (!_mbglMap) {
return;
}
+
+ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)])
{
@@ -5058,7 +5582,7 @@ public:
if (annotationView)
{
- annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
+ annotationView.center = MGLPointRounded([self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self]);
}
}
@@ -5176,7 +5700,7 @@ public:
}
else
{
- userPoint = [self convertCoordinate:self.userLocation.coordinate toPointToView:self];
+ userPoint = MGLPointRounded([self convertCoordinate:self.userLocation.coordinate toPointToView:self]);
}
if ( ! annotationView.superview)
@@ -5429,7 +5953,7 @@ public:
return _annotationViewReuseQueueByIdentifier[identifier];
}
-class MBGLView : public mbgl::View, public mbgl::Backend
+class MBGLView : public mbgl::RendererBackend, public mbgl::MapObserver
{
public:
MBGLView(MGLMapView* nativeView_) : nativeView(nativeView_) {
@@ -5457,6 +5981,10 @@ public:
}
}
+ mbgl::Size getFramebufferSize() const override {
+ return nativeView.framebufferSize;
+ }
+
void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override {
bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated;
[nativeView cameraWillChangeAnimated:animated];
@@ -5527,7 +6055,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.");
@@ -5541,11 +6069,6 @@ public:
return reinterpret_cast<mbgl::gl::ProcAddress>(symbol);
}
- void invalidate() override
- {
- [nativeView setNeedsGLDisplay];
- }
-
void activate() override
{
if (activationCount++)
@@ -5713,4 +6236,19 @@ private:
self.pitchEnabled = allowsTilting;
}
++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingShowsHeading
+{
+ return [NSSet setWithObject:@"showsUserHeadingIndicator"];
+}
+
+- (BOOL)showsHeading
+{
+ return self.showsUserHeadingIndicator;
+}
+
+- (void)setShowsHeading:(BOOL)showsHeading
+{
+ self.showsUserHeadingIndicator = showsHeading;
+}
+
@end
diff --git a/platform/ios/src/MGLMapView_Private.h b/platform/ios/src/MGLMapView_Private.h
index 4e2765377c..482ab55c5e 100644
--- a/platform/ios/src/MGLMapView_Private.h
+++ b/platform/ios/src/MGLMapView_Private.h
@@ -2,6 +2,7 @@
namespace mbgl {
class Map;
+ class Renderer;
}
/// Minimum size of an annotation’s accessibility element.
@@ -17,6 +18,8 @@ extern const CGSize MGLAnnotationAccessibilityElementMinimumSize;
- (mbgl::Map *)mbglMap;
+- (mbgl::Renderer *)renderer;
+
/** Returns whether the map view is currently loading or processing any assets required to render the map */
- (BOOL)isFullyLoaded;
diff --git a/platform/ios/src/MGLSDKUpdateChecker.mm b/platform/ios/src/MGLSDKUpdateChecker.mm
index ab4ef7be86..bb61e2b595 100644
--- a/platform/ios/src/MGLSDKUpdateChecker.mm
+++ b/platform/ios/src/MGLSDKUpdateChecker.mm
@@ -29,7 +29,7 @@
NSString *latestVersion = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
latestVersion = [latestVersion stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (![currentVersion isEqualToString:latestVersion]) {
- NSString *updateAvailable = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"SDK_UPDATE_AVAILABLE", nil, nil, @"Mapbox iOS SDK version %@ is now available:", @"Developer-only SDK update notification; {latest version, in format x.x.x}"), latestVersion];
+ NSString *updateAvailable = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"SDK_UPDATE_AVAILABLE", nil, nil, @"Mapbox Maps SDK for iOS version %@ is now available:", @"Developer-only SDK update notification; {latest version, in format x.x.x}"), latestVersion];
NSLog(@"%@ https://github.com/mapbox/mapbox-gl-native/releases/tag/ios-v%@", updateAvailable, latestVersion);
}
}] resume];
diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm
index cd88c1e08e..139dffdfab 100644
--- a/platform/ios/src/MGLScaleBar.mm
+++ b/platform/ios/src/MGLScaleBar.mm
@@ -175,10 +175,15 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
return [self usesMetricSystem] ? self.metersPerPoint : self.metersPerPoint * MGLFeetPerMeter;
}
-#pragma mark - Convenient methods
+#pragma mark - Convenience methods
- (BOOL)usesRightToLeftLayout {
- return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
+ // semanticContentAttribute is iOS 9+
+ if ([self.superview respondsToSelector:@selector(semanticContentAttribute)]) {
+ return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
+ } else {
+ return UIApplication.sharedApplication.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
+ }
}
- (BOOL)usesMetricSystem {
@@ -188,9 +193,9 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
- (MGLRow)preferredRow {
CLLocationDistance maximumDistance = [self maximumWidth] * [self unitsPerPoint];
- MGLRow row;
BOOL useMetric = [self usesMetricSystem];
+ MGLRow row = useMetric ? MGLMetricTable[0] : MGLImperialTable[0];
NSUInteger count = useMetric
? sizeof(MGLMetricTable) / sizeof(MGLMetricTable[0])
: sizeof(MGLImperialTable) / sizeof(MGLImperialTable[0]);
@@ -225,9 +230,6 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
CGRectGetMinY(self.frame),
size.width,
size.height);
-
- [self invalidateIntrinsicContentSize];
- [self setNeedsLayout];
}
- (void)updateVisibility {
@@ -244,7 +246,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
CGFloat alpha = maximumDistance > allowedDistance ? .0f : 1.0f;
- if(self.alpha != alpha) {
+ if (self.alpha != alpha) {
[UIView animateWithDuration:.2f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.alpha = alpha;
} completion:nil];
@@ -334,7 +336,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
}
- (void)layoutBars {
- CGFloat barWidth = (CGRectGetWidth(self.bounds) - self.borderWidth * 2.0f) / self.bars.count;
+ CGFloat barWidth = round((CGRectGetWidth(self.bounds) - self.borderWidth * 2.0f) / self.bars.count);
NSUInteger i = 0;
for (UIView *bar in self.bars) {
@@ -357,11 +359,11 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
}
- (void)layoutLabels {
- CGFloat barWidth = self.bounds.size.width / self.bars.count;
+ CGFloat barWidth = round(self.bounds.size.width / self.bars.count);
BOOL RTL = [self usesRightToLeftLayout];
NSUInteger i = RTL ? self.bars.count : 0;
for (MGLScaleBarLabel *label in self.labels) {
- CGFloat xPosition = barWidth * i - CGRectGetMidX(label.bounds) + self.borderWidth;
+ CGFloat xPosition = round(barWidth * i - CGRectGetMidX(label.bounds) + self.borderWidth);
label.frame = CGRectMake(xPosition, 0,
CGRectGetWidth(label.bounds),
CGRectGetHeight(label.bounds));
diff --git a/platform/ios/src/MGLUserLocation.h b/platform/ios/src/MGLUserLocation.h
index c41c3ee7fd..4e01cf00c9 100644
--- a/platform/ios/src/MGLUserLocation.h
+++ b/platform/ios/src/MGLUserLocation.h
@@ -1,6 +1,7 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
+#import "MGLFoundation.h"
#import "MGLAnnotation.h"
NS_ASSUME_NONNULL_BEGIN
@@ -8,9 +9,10 @@ NS_ASSUME_NONNULL_BEGIN
/**
The MGLUserLocation class defines a specific type of annotation that identifies
the user’s current location. You do not create instances of this class
- directly. Instead, you retrieve an existing MGLUserLocation object from the
+ directly. Instead, you retrieve an existing `MGLUserLocation` object from the
`userLocation` property of the map view displayed in your application.
*/
+MGL_EXPORT
@interface MGLUserLocation : NSObject <MGLAnnotation, NSSecureCoding>
#pragma mark Determining the User’s Position
@@ -18,8 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
The current location of the device. (read-only)
- This property contains `nil` if the map view is not currently showing the user
- location or if the user’s location has not yet been determined.
+ This property returns `nil` if the user’s location has not yet been determined.
*/
@property (nonatomic, readonly, nullable) CLLocation *location;
@@ -33,7 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
The heading of the user location. (read-only)
This property is `nil` if the user location tracking mode is not
- `MGLUserTrackingModeFollowWithHeading`.
+ `MGLUserTrackingModeFollowWithHeading` or if
+ `MGLMapView.showsUserHeadingIndicator` is disabled.
*/
@property (nonatomic, readonly, nullable) CLHeading *heading;
diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m
index 1c9649c09e..074d138a72 100644
--- a/platform/ios/src/MGLUserLocation.m
+++ b/platform/ios/src/MGLUserLocation.m
@@ -19,7 +19,6 @@ NS_ASSUME_NONNULL_END
{
if (self = [super init])
{
- _location = [[CLLocation alloc] initWithLatitude:MAXFLOAT longitude:MAXFLOAT];
_mapView = mapView;
}
@@ -102,7 +101,7 @@ NS_ASSUME_NONNULL_END
- (CLLocationCoordinate2D)coordinate
{
- return self.location.coordinate;
+ return _location ? _location.coordinate : kCLLocationCoordinate2DInvalid;
}
- (NSString *)title
diff --git a/platform/ios/src/MGLUserLocationAnnotationView.h b/platform/ios/src/MGLUserLocationAnnotationView.h
index 4b36236b8d..4d95f39cf3 100644
--- a/platform/ios/src/MGLUserLocationAnnotationView.h
+++ b/platform/ios/src/MGLUserLocationAnnotationView.h
@@ -1,6 +1,7 @@
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
+#import "MGLFoundation.h"
#import "MGLAnnotationView.h"
NS_ASSUME_NONNULL_BEGIN
@@ -9,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
@class MGLUserLocation;
/** View representing an `MGLUserLocation` on screen. */
+MGL_EXPORT
@interface MGLUserLocationAnnotationView : MGLAnnotationView
/**
diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.h b/platform/ios/src/MGLUserLocationHeadingArrowLayer.h
new file mode 100644
index 0000000000..6c01356944
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.h
@@ -0,0 +1,11 @@
+#import <QuartzCore/QuartzCore.h>
+#import "MGLUserLocationAnnotationView.h"
+#import "MGLUserLocationHeadingIndicator.h"
+
+@interface MGLUserLocationHeadingArrowLayer : CAShapeLayer <MGLUserLocationHeadingIndicator>
+
+- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView;
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy;
+- (void)updateTintColor:(CGColorRef)color;
+
+@end
diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.m b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m
new file mode 100644
index 0000000000..912ce30c35
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m
@@ -0,0 +1,59 @@
+#import "MGLUserLocationHeadingArrowLayer.h"
+
+#import "MGLFaux3DUserLocationAnnotationView.h"
+#import "MGLGeometry.h"
+
+const CGFloat MGLUserLocationHeadingArrowSize = 6;
+
+@implementation MGLUserLocationHeadingArrowLayer
+
+- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView
+{
+ CGFloat size = userLocationView.bounds.size.width + MGLUserLocationHeadingArrowSize;
+
+ self = [super init];
+ self.bounds = CGRectMake(0, 0, size, size);
+ self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds));
+ self.path = [self arrowPath];
+ self.fillColor = userLocationView.tintColor.CGColor;
+ self.shouldRasterize = YES;
+ self.rasterizationScale = UIScreen.mainScreen.scale;
+ self.drawsAsynchronously = YES;
+
+ self.strokeColor = UIColor.whiteColor.CGColor;
+ self.lineWidth = 1.0;
+ self.lineJoin = kCALineJoinRound;
+
+ return self;
+}
+
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy
+{
+ // unimplemented
+}
+
+- (void)updateTintColor:(CGColorRef)color
+{
+ self.fillColor = color;
+}
+
+- (CGPathRef)arrowPath {
+ CGFloat center = roundf(CGRectGetMidX(self.bounds));
+ CGFloat size = MGLUserLocationHeadingArrowSize;
+
+ CGPoint top = CGPointMake(center, 0);
+ CGPoint left = CGPointMake(center - size, size);
+ CGPoint right = CGPointMake(center + size, size);
+ CGPoint middle = CGPointMake(center, size / M_PI);
+
+ UIBezierPath *bezierPath = [UIBezierPath bezierPath];
+ [bezierPath moveToPoint:top];
+ [bezierPath addLineToPoint:left];
+ [bezierPath addQuadCurveToPoint:right controlPoint:middle];
+ [bezierPath addLineToPoint:top];
+ [bezierPath closePath];
+
+ return bezierPath.CGPath;
+}
+
+@end
diff --git a/platform/ios/src/MGLUserLocationHeadingBeamLayer.h b/platform/ios/src/MGLUserLocationHeadingBeamLayer.h
new file mode 100644
index 0000000000..93f8ea17ab
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingBeamLayer.h
@@ -0,0 +1,11 @@
+#import <QuartzCore/QuartzCore.h>
+#import "MGLUserLocationAnnotationView.h"
+#import "MGLUserLocationHeadingIndicator.h"
+
+@interface MGLUserLocationHeadingBeamLayer : CALayer <MGLUserLocationHeadingIndicator>
+
+- (MGLUserLocationHeadingBeamLayer *)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView;
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy;
+- (void)updateTintColor:(CGColorRef)color;
+
+@end
diff --git a/platform/ios/src/MGLUserLocationHeadingBeamLayer.m b/platform/ios/src/MGLUserLocationHeadingBeamLayer.m
new file mode 100644
index 0000000000..efe7e4db93
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingBeamLayer.m
@@ -0,0 +1,104 @@
+#import "MGLUserLocationHeadingBeamLayer.h"
+
+#import "MGLFaux3DUserLocationAnnotationView.h"
+#import "MGLGeometry.h"
+
+@implementation MGLUserLocationHeadingBeamLayer
+{
+ CAShapeLayer *_maskLayer;
+}
+
+- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView
+{
+ CGFloat size = MGLUserLocationAnnotationHaloSize;
+
+ self = [super init];
+ self.bounds = CGRectMake(0, 0, size, size);
+ self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds));
+ self.contents = (__bridge id)[self gradientImageWithTintColor:userLocationView.tintColor.CGColor];
+ self.contentsGravity = kCAGravityBottom;
+ self.contentsScale = UIScreen.mainScreen.scale;
+ self.opacity = 0.4;
+ self.shouldRasterize = YES;
+ self.rasterizationScale = UIScreen.mainScreen.scale;
+ self.drawsAsynchronously = YES;
+
+ _maskLayer = [CAShapeLayer layer];
+ _maskLayer.frame = self.bounds;
+ _maskLayer.path = [self clippingMaskForAccuracy:0];
+ self.mask = _maskLayer;
+
+ return self;
+}
+
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy
+{
+ // recalculate the clipping mask based on updated accuracy
+ _maskLayer.path = [self clippingMaskForAccuracy:accuracy];
+}
+
+- (void)updateTintColor:(CGColorRef)color
+{
+ // redraw the raw tinted gradient
+ self.contents = (__bridge id)[self gradientImageWithTintColor:color];
+}
+
+- (CGImageRef)gradientImageWithTintColor:(CGColorRef)tintColor
+{
+ UIImage *image;
+
+ CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0;
+
+ UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0);
+
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+ CGContextRef context = UIGraphicsGetCurrentContext();
+
+ // gradient from the tint color to no-alpha tint color
+ CGFloat gradientLocations[] = {0.0, 1.0};
+ CGGradientRef gradient = CGGradientCreateWithColors(
+ colorSpace,
+ (__bridge CFArrayRef)@[(__bridge id)tintColor,
+ (id)CFBridgingRelease(CGColorCreateCopyWithAlpha(tintColor, 0))],
+ gradientLocations);
+
+ // draw the gradient from the center point to the edge (full halo radius)
+ CGPoint centerPoint = CGPointMake(haloRadius, haloRadius);
+ CGContextDrawRadialGradient(context, gradient,
+ centerPoint, 0.0,
+ centerPoint, haloRadius,
+ kNilOptions);
+
+ image = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ CGGradientRelease(gradient);
+ CGColorSpaceRelease(colorSpace);
+
+ return image.CGImage;
+}
+
+- (CGPathRef)clippingMaskForAccuracy:(CGFloat)accuracy
+{
+ // size the mask using accuracy, but keep within a good display range
+ CGFloat clippingDegrees = 90 - accuracy;
+ clippingDegrees = fmin(clippingDegrees, 70); // most accurate
+ clippingDegrees = fmax(clippingDegrees, 10); // least accurate
+
+ CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize);
+ UIBezierPath *ovalPath = UIBezierPath.bezierPath;
+
+ // clip the oval to ± incoming accuracy degrees (converted to radians), from the top
+ [ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))
+ radius:CGRectGetWidth(ovalRect) / 2.0
+ startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees)
+ endAngle:MGLRadiansFromDegrees(-clippingDegrees)
+ clockwise:YES];
+
+ [ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))];
+ [ovalPath closePath];
+
+ return ovalPath.CGPath;
+}
+
+@end
diff --git a/platform/ios/src/MGLUserLocationHeadingIndicator.h b/platform/ios/src/MGLUserLocationHeadingIndicator.h
new file mode 100644
index 0000000000..61476b96a2
--- /dev/null
+++ b/platform/ios/src/MGLUserLocationHeadingIndicator.h
@@ -0,0 +1,10 @@
+#import <QuartzCore/QuartzCore.h>
+#import "MGLUserLocationAnnotationView.h"
+
+@protocol MGLUserLocationHeadingIndicator <NSObject>
+
+- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView;
+- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy;
+- (void)updateTintColor:(CGColorRef)color;
+
+@end
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
index abe16cc3ee..ce9c4965d7 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -51,6 +51,8 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLTileSource.h"
#import "MGLVectorSource.h"
#import "MGLShapeSource.h"
+#import "MGLAbstractShapeSource.h"
+#import "MGLComputedShapeSource.h"
#import "MGLRasterSource.h"
#import "MGLImageSource.h"
#import "MGLTilePyramidOfflineRegion.h"
@@ -60,3 +62,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "NSValue+MGLAdditions.h"
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"
+#import "MGLMapSnapshotter.h"
diff --git a/platform/ios/src/UIImage+MGLAdditions.h b/platform/ios/src/UIImage+MGLAdditions.h
index 6e15e07cb5..22bb740242 100644
--- a/platform/ios/src/UIImage+MGLAdditions.h
+++ b/platform/ios/src/UIImage+MGLAdditions.h
@@ -8,6 +8,8 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image *)styleImage;
+- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage scale:(CGFloat)scale;
+
- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier;
- (mbgl::PremultipliedImage)mgl_premultipliedImage;
diff --git a/platform/ios/src/UIImage+MGLAdditions.mm b/platform/ios/src/UIImage+MGLAdditions.mm
index 5e28d18190..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;
}
@@ -22,6 +22,19 @@
return self;
}
+- (nullable instancetype)initWithMGLPremultipliedImage:(const mbgl::PremultipliedImage&&)mbglImage scale:(CGFloat)scale
+{
+ CGImageRef image = CGImageCreateWithMGLPremultipliedImage(mbglImage.clone());
+ if (!image) {
+ return nil;
+ }
+
+ self = [self initWithCGImage:image scale:scale orientation:UIImageOrientationUp];
+
+ CGImageRelease(image);
+ return self;
+}
+
- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier {
BOOL isTemplate = self.renderingMode == UIImageRenderingModeAlwaysTemplate;
return std::make_unique<mbgl::style::Image>([identifier UTF8String],
diff --git a/platform/ios/test/MGLMapAccessibilityElementTests.m b/platform/ios/test/MGLMapAccessibilityElementTests.m
new file mode 100644
index 0000000000..5c79d85de1
--- /dev/null
+++ b/platform/ios/test/MGLMapAccessibilityElementTests.m
@@ -0,0 +1,87 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+#import "../../ios/src/MGLMapAccessibilityElement.h"
+
+@interface MGLMapAccessibilityElementTests : XCTestCase
+@end
+
+@implementation MGLMapAccessibilityElementTests
+
+- (void)testFeatureLabels {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ feature.attributes = @{
+ @"name": @"Local",
+ @"name_en": @"English",
+ @"name_es": @"Spanish",
+ @"name_fr": @"French",
+ @"name_tlh": @"Klingon",
+ };
+ MGLFeatureAccessibilityElement *element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
+ XCTAssertEqualObjects(element.accessibilityLabel, @"English", @"Accessibility label should be localized.");
+
+ feature.attributes = @{
+ @"name": @"Цинциннати",
+ @"name_en": @"Цинциннати",
+ };
+ element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
+ XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized.");
+}
+
+- (void)testPlaceFeatureValues {
+ MGLPointFeature *feature = [[MGLPointFeature alloc] init];
+ feature.attributes = @{
+ @"type": @"village_green",
+ };
+ MGLPlaceFeatureAccessibilityElement *element = [[MGLPlaceFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
+ XCTAssertEqualObjects(element.accessibilityValue, @"village green");
+
+ feature = [[MGLPointFeature alloc] init];
+ feature.attributes = @{
+ @"maki": @"cat",
+ };
+ element = [[MGLPlaceFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
+ XCTAssertEqualObjects(element.accessibilityValue, @"cat");
+
+ feature = [[MGLPointFeature alloc] init];
+ feature.attributes = @{
+ @"elevation_ft": @31337,
+ @"elevation_m": @1337,
+ };
+ element = [[MGLPlaceFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
+ XCTAssertEqualObjects(element.accessibilityValue, @"31,337 feet");
+}
+
+- (void)testRoadFeatureValues {
+ CLLocationCoordinate2D coordinates[] = {
+ CLLocationCoordinate2DMake(0, 0),
+ CLLocationCoordinate2DMake(0, 1),
+ CLLocationCoordinate2DMake(1, 2),
+ CLLocationCoordinate2DMake(2, 2),
+ };
+ MGLPolylineFeature *roadFeature = [MGLPolylineFeature polylineWithCoordinates:coordinates count:sizeof(coordinates) / sizeof(coordinates[0])];
+ roadFeature.attributes = @{
+ @"ref": @"42",
+ @"oneway": @"true",
+ };
+ MGLRoadFeatureAccessibilityElement *element = [[MGLRoadFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:roadFeature];
+ XCTAssertEqualObjects(element.accessibilityValue, @"Route 42, One way, southwest to northeast");
+
+ CLLocationCoordinate2D opposingCoordinates[] = {
+ CLLocationCoordinate2DMake(2, 1),
+ CLLocationCoordinate2DMake(1, 0),
+ };
+ MGLPolylineFeature *opposingRoadFeature = [MGLPolylineFeature polylineWithCoordinates:opposingCoordinates count:sizeof(opposingCoordinates) / sizeof(opposingCoordinates[0])];
+ opposingRoadFeature.attributes = @{
+ @"ref": @"42",
+ @"oneway": @"true",
+ };
+ MGLMultiPolylineFeature *dividedRoadFeature = [MGLMultiPolylineFeature multiPolylineWithPolylines:@[roadFeature, opposingRoadFeature]];
+ dividedRoadFeature.attributes = @{
+ @"ref": @"42",
+ };
+ element = [[MGLRoadFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:dividedRoadFeature];
+ XCTAssertEqualObjects(element.accessibilityValue, @"Route 42, Divided road, southwest to northeast");
+}
+
+@end
diff --git a/platform/ios/uitest/KIF b/platform/ios/uitest/KIF
-Subproject 973a4cb653b54c3e8b2c0681f4097568ff0ac34
+Subproject c40b1048a6f35c6fd90376846a1a933844516b3
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/OHHTTPStubs b/platform/ios/uitest/OHHTTPStubs
-Subproject deed01a1592210a4c37f3f5c5f2b32fe0e41c60
+Subproject 4dc6f36375f78c0b3cfe58d90bb8a4e21df5196
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 b/platform/ios/vendor/SMCalloutView
deleted file mode 160000
-Subproject d6ecaba377c9f963aef630faf86e3b8f8cdb88d
diff --git a/platform/ios/vendor/SMCalloutView/SMCalloutView.h b/platform/ios/vendor/SMCalloutView/SMCalloutView.h
new file mode 100755
index 0000000000..0b14913626
--- /dev/null
+++ b/platform/ios/vendor/SMCalloutView/SMCalloutView.h
@@ -0,0 +1,205 @@
+#import <UIKit/UIKit.h>
+#import <QuartzCore/QuartzCore.h>
+
+/*
+
+SMCalloutView
+-------------
+Created by Nick Farina (nfarina@gmail.com)
+Version 2.1.5
+
+*/
+
+/// options for which directions the callout is allowed to "point" in.
+typedef NS_OPTIONS(NSUInteger, MGLSMCalloutArrowDirection) {
+ MGLSMCalloutArrowDirectionUp = 1 << 0,
+ MGLSMCalloutArrowDirectionDown = 1 << 1,
+ MGLSMCalloutArrowDirectionAny = MGLSMCalloutArrowDirectionUp | MGLSMCalloutArrowDirectionDown
+};
+
+/// options for the callout present/dismiss animation
+typedef NS_ENUM(NSInteger, MGLSMCalloutAnimation) {
+ /// the "bounce" animation we all know and love from @c UIAlertView
+ MGLSMCalloutAnimationBounce,
+ /// a simple fade in or out
+ MGLSMCalloutAnimationFade,
+ /// grow or shrink linearly, like in the iPad Calendar app
+ MGLSMCalloutAnimationStretch
+};
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// when delaying our popup in order to scroll content into view, you can use this amount to match the
+/// animation duration of UIScrollView when using @c -setContentOffset:animated.
+extern NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView;
+
+@protocol MGLSMCalloutViewDelegate;
+@class MGLSMCalloutBackgroundView;
+
+//
+// Callout view.
+//
+
+// iOS 10+ expects CAAnimationDelegate to be set explicitly.
+#if __IPHONE_OS_VERSION_MAX_ALLOWED < 100000
+@interface MGLSMCalloutView : UIView
+#else
+@interface MGLSMCalloutView : UIView <CAAnimationDelegate>
+#endif
+
+@property (nonatomic, weak, nullable) id<MGLSMCalloutViewDelegate> delegate;
+/// title/titleView relationship mimics UINavigationBar.
+@property (nonatomic, copy, nullable) NSString *title;
+@property (nonatomic, copy, nullable) NSString *subtitle;
+
+/// Left accessory view for the call out
+@property (nonatomic, strong, nullable) UIView *leftAccessoryView;
+/// Right accessoty view for the call out
+@property (nonatomic, strong, nullable) UIView *rightAccessoryView;
+/// Default @c SMCalloutArrowDirectionDown
+@property (nonatomic, assign) MGLSMCalloutArrowDirection permittedArrowDirection;
+/// The current arrow direction
+@property (nonatomic, readonly) MGLSMCalloutArrowDirection currentArrowDirection;
+/// if the @c UIView you're constraining to has portions that are overlapped by nav bar, tab bar, etc. you'll need to tell us those insets.
+@property (nonatomic, assign) UIEdgeInsets constrainedInsets;
+/// default is @c SMCalloutMaskedBackgroundView, or @c SMCalloutDrawnBackgroundView when using @c SMClassicCalloutView
+@property (nonatomic, strong) MGLSMCalloutBackgroundView *backgroundView;
+
+/**
+ @brief Custom title view.
+
+ @disucssion Keep in mind that @c SMCalloutView calls @c -sizeThatFits on titleView/subtitleView if defined, so your view
+ may be resized as a result of that (especially if you're using @c UILabel/UITextField). You may want to subclass and override @c -sizeThatFits, or just wrap your view in a "generic" @c UIView if you do not want it to be auto-sized.
+
+ @warning If this is set, the respective @c title property will be ignored.
+ */
+@property (nonatomic, strong, nullable) UIView *titleView;
+
+/**
+ @brief Custom subtitle view.
+
+ @discussion Keep in mind that @c SMCalloutView calls @c -sizeThatFits on subtitleView if defined, so your view
+ may be resized as a result of that (especially if you're using @c UILabel/UITextField). You may want to subclass and override @c -sizeThatFits, or just wrap your view in a "generic" @c UIView if you do not want it to be auto-sized.
+
+ @warning If this is set, the respective @c subtitle property will be ignored.
+ */
+@property (nonatomic, strong, nullable) UIView *subtitleView;
+
+/// Custom "content" view that can be any width/height. If this is set, title/subtitle/titleView/subtitleView are all ignored.
+@property (nonatomic, retain, nullable) UIView *contentView;
+
+/// Custom content view margin
+@property (nonatomic, assign) UIEdgeInsets contentViewInset;
+
+/// calloutOffset is the offset in screen points from the top-middle of the target view, where the anchor of the callout should be shown.
+@property (nonatomic, assign) CGPoint calloutOffset;
+
+/// default SMCalloutAnimationBounce, SMCalloutAnimationFade respectively
+@property (nonatomic, assign) MGLSMCalloutAnimation presentAnimation, dismissAnimation;
+
+/// Returns a new instance of SMCalloutView if running on iOS 7 or better, otherwise a new instance of SMClassicCalloutView if available.
++ (MGLSMCalloutView *)platformCalloutView;
+
+/**
+ @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 bounds of the given view. Optionally scrolls the given rect into view (plus margins)
+ if @c -delegate is set and responds to @c -delayForRepositionWithSize.
+
+ @param rect @c CGRect to present the view from
+ @param view view to 'constrain' the @c constrainedView to
+ @param constrainedView @c UIView to be constrainted in @c view
+ @param animated @c BOOL if presentation should be animated
+ */
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView 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.
+ @note Be aware that you'll have to direct your own touches to any accessory views, since CALayer doesn't relay touch events.
+
+ @param rect @c CGRect to present the view from
+ @param layer layer to 'constrain' the @c constrainedLayer to
+ @param constrainedLayer @c CALayer to be constrained in @c layer
+ @param animated @c BOOL if presentation should be animated
+ */
+- (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer constrainedToLayer:(CALayer *)constrainedLayer animated:(BOOL)animated;
+
+/**
+ Dismiss the callout view
+
+ @param animated @c BOOL if dismissal should be animated
+ */
+- (void)dismissCalloutAnimated:(BOOL)animated;
+
+/// For subclassers. You can override this method to provide your own custom animation for presenting/dismissing the callout.
+- (CAAnimation *)animationWithType:(MGLSMCalloutAnimation)type presenting:(BOOL)presenting;
+
+@end
+
+//
+// Background view - default draws the iOS 7 system background style (translucent white with rounded arrow).
+//
+
+/// Abstract base class
+@interface MGLSMCalloutBackgroundView : UIView
+/// indicates where the tip of the arrow should be drawn, as a pixel offset
+@property (nonatomic, assign) CGPoint arrowPoint;
+/// will be set by the callout when the callout is in a highlighted state
+@property (nonatomic, assign) BOOL highlighted;
+/// returns an optional layer whose contents should mask the callout view's contents (not honored by @c SMClassicCalloutView )
+@property (nonatomic, assign) CALayer *contentMask;
+/// height of the callout "arrow"
+@property (nonatomic, assign) CGFloat anchorHeight;
+/// the smallest possible distance from the edge of our control to the "tip" of the anchor, from either left or right
+@property (nonatomic, assign) CGFloat anchorMargin;
+@end
+
+/// Default for iOS 7, this reproduces the "masked" behavior of the iOS 7-style callout view.
+/// Accessories are masked by the shape of the callout (including the arrow itself).
+@interface MGLSMCalloutMaskedBackgroundView : MGLSMCalloutBackgroundView
+@end
+
+//
+// Delegate methods
+//
+
+@protocol MGLSMCalloutViewDelegate <NSObject>
+@optional
+
+/// Controls whether the callout "highlights" when pressed. default YES. You must also respond to @c -calloutViewClicked below.
+/// Not honored by @c SMClassicCalloutView.
+- (BOOL)calloutViewShouldHighlight:(MGLSMCalloutView *)calloutView;
+
+/// Called when the callout view is clicked. Not honored by @c SMClassicCalloutView.
+- (void)calloutViewClicked:(MGLSMCalloutView *)calloutView;
+
+/**
+ Called when the callout view detects that it will be outside the constrained view when it appears,
+ or if the target rect was already outside the constrained view. You can implement this selector
+ to respond to this situation by repositioning your content first in order to make everything visible.
+ The @c CGSize passed is the calculated offset necessary to make everything visible (plus a nice margin).
+ It expects you to return the amount of time you need to reposition things so the popup can be delayed.
+ Typically you would return @c kSMCalloutViewRepositionDelayForUIScrollView if you're repositioning by calling @c [UIScrollView @c setContentOffset:animated:].
+
+ @param calloutView the @c SMCalloutView to reposition
+ @param offset caluclated offset necessary to make everything visible
+ @returns @c NSTimeInterval to delay the repositioning
+ */
+- (NSTimeInterval)calloutView:(MGLSMCalloutView *)calloutView delayForRepositionWithSize:(CGSize)offset;
+
+/// Called before the callout view appears on screen, or before the appearance animation will start.
+- (void)calloutViewWillAppear:(MGLSMCalloutView *)calloutView;
+
+/// Called after the callout view appears on screen, or after the appearance animation is complete.
+- (void)calloutViewDidAppear:(MGLSMCalloutView *)calloutView;
+
+/// Called before the callout view is removed from the screen, or before the disappearance animation is complete.
+- (void)calloutViewWillDisappear:(MGLSMCalloutView *)calloutView;
+
+/// Called after the callout view is removed from the screen, or after the disappearance animation is complete.
+- (void)calloutViewDidDisappear:(MGLSMCalloutView *)calloutView;
+
+NS_ASSUME_NONNULL_END
+@end
diff --git a/platform/ios/vendor/SMCalloutView/SMCalloutView.m b/platform/ios/vendor/SMCalloutView/SMCalloutView.m
new file mode 100755
index 0000000000..9631ca0367
--- /dev/null
+++ b/platform/ios/vendor/SMCalloutView/SMCalloutView.m
@@ -0,0 +1,851 @@
+#import "SMCalloutView.h"
+
+//
+// UIView frame helpers - we do a lot of UIView frame fiddling in this class; these functions help keep things readable.
+//
+
+@interface UIView (SMFrameAdditions)
+@property (nonatomic, assign) CGPoint frameOrigin;
+@property (nonatomic, assign) CGSize frameSize;
+@property (nonatomic, assign) CGFloat frameX, frameY, frameWidth, frameHeight; // normal rect properties
+@property (nonatomic, assign) CGFloat frameLeft, frameTop, frameRight, frameBottom; // these will stretch/shrink the rect
+@end
+
+//
+// Callout View.
+//
+
+#define CALLOUT_DEFAULT_CONTAINER_HEIGHT 44 // height of just the main portion without arrow
+#define CALLOUT_SUB_DEFAULT_CONTAINER_HEIGHT 52 // height of just the main portion without arrow (when subtitle is present)
+#define CALLOUT_MIN_WIDTH 61 // minimum width of system callout
+#define TITLE_HMARGIN 12 // the title/subtitle view's normal horizontal margin from the edges of our callout view or from the accessories
+#define TITLE_TOP 11 // the top of the title view when no subtitle is present
+#define TITLE_SUB_TOP 4 // the top of the title view when a subtitle IS present
+#define TITLE_HEIGHT 21 // title height, fixed
+#define SUBTITLE_TOP 28 // the top of the subtitle, when present
+#define SUBTITLE_HEIGHT 15 // subtitle height, fixed
+#define BETWEEN_ACCESSORIES_MARGIN 7 // margin between accessories when no title/subtitle is present
+#define TOP_ANCHOR_MARGIN 13 // all the above measurements assume a bottom anchor! if we're pointing "up" we'll need to add this top margin to everything.
+#define COMFORTABLE_MARGIN 10 // when we try to reposition content to be visible, we'll consider this margin around your target rect
+
+NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView = 1.0/3.0;
+
+@interface MGLSMCalloutView ()
+@property (nonatomic, strong) UIButton *containerView; // for masking and interaction
+@property (nonatomic, strong) UILabel *titleLabel, *subtitleLabel;
+@property (nonatomic, assign) MGLSMCalloutArrowDirection currentArrowDirection;
+@property (nonatomic, assign) BOOL popupCancelled;
+@end
+
+@implementation MGLSMCalloutView
+
++ (MGLSMCalloutView *)platformCalloutView {
+ // MGL: Mapbox does not need or include the custom flavor, so this is modified to just use SMCalloutView.
+ return [MGLSMCalloutView new];
+}
+
+- (id)initWithFrame:(CGRect)frame {
+ if (self = [super initWithFrame:frame]) {
+ self.permittedArrowDirection = MGLSMCalloutArrowDirectionDown;
+ self.presentAnimation = MGLSMCalloutAnimationBounce;
+ self.dismissAnimation = MGLSMCalloutAnimationFade;
+ self.backgroundColor = [UIColor clearColor];
+ self.containerView = [UIButton new];
+ self.containerView.isAccessibilityElement = NO;
+ self.isAccessibilityElement = NO;
+ self.contentViewInset = UIEdgeInsetsMake(12, 12, 12, 12);
+
+ [self.containerView addTarget:self action:@selector(highlightIfNecessary) forControlEvents:UIControlEventTouchDown | UIControlEventTouchDragInside];
+ [self.containerView addTarget:self action:@selector(unhighlightIfNecessary) forControlEvents:UIControlEventTouchDragOutside | UIControlEventTouchCancel | UIControlEventTouchUpOutside | UIControlEventTouchUpInside];
+ [self.containerView addTarget:self action:@selector(calloutClicked) forControlEvents:UIControlEventTouchUpInside];
+ }
+ return self;
+}
+
+- (BOOL)supportsHighlighting {
+ if (![self.delegate respondsToSelector:@selector(calloutViewClicked:)])
+ return NO;
+ if ([self.delegate respondsToSelector:@selector(calloutViewShouldHighlight:)])
+ return [self.delegate calloutViewShouldHighlight:self];
+ return YES;
+}
+
+- (void)highlightIfNecessary { if (self.supportsHighlighting) self.backgroundView.highlighted = YES; }
+- (void)unhighlightIfNecessary { if (self.supportsHighlighting) self.backgroundView.highlighted = NO; }
+
+- (void)calloutClicked {
+ if ([self.delegate respondsToSelector:@selector(calloutViewClicked:)])
+ [self.delegate calloutViewClicked:self];
+}
+
+- (UIView *)titleViewOrDefault {
+ if (self.titleView)
+ // if you have a custom title view defined, return that.
+ return self.titleView;
+ else {
+ if (!self.titleLabel) {
+ // create a default titleView
+ self.titleLabel = [UILabel new];
+ self.titleLabel.frameHeight = TITLE_HEIGHT;
+ self.titleLabel.opaque = NO;
+ self.titleLabel.backgroundColor = [UIColor clearColor];
+ self.titleLabel.font = [UIFont systemFontOfSize:17];
+ self.titleLabel.textColor = [UIColor blackColor];
+ }
+ return self.titleLabel;
+ }
+}
+
+- (UIView *)subtitleViewOrDefault {
+ if (self.subtitleView)
+ // if you have a custom subtitle view defined, return that.
+ return self.subtitleView;
+ else {
+ if (!self.subtitleLabel) {
+ // create a default subtitleView
+ self.subtitleLabel = [UILabel new];
+ self.subtitleLabel.frameHeight = SUBTITLE_HEIGHT;
+ self.subtitleLabel.opaque = NO;
+ self.subtitleLabel.backgroundColor = [UIColor clearColor];
+ self.subtitleLabel.font = [UIFont systemFontOfSize:12];
+ self.subtitleLabel.textColor = [UIColor blackColor];
+ }
+ return self.subtitleLabel;
+ }
+}
+
+- (MGLSMCalloutBackgroundView *)backgroundView {
+ // create our default background on first access only if it's nil, since you might have set your own background anyway.
+ return _backgroundView ? _backgroundView : (_backgroundView = [self defaultBackgroundView]);
+}
+
+- (MGLSMCalloutBackgroundView *)defaultBackgroundView {
+ return [MGLSMCalloutMaskedBackgroundView new];
+}
+
+- (void)rebuildSubviews {
+ // remove and re-add our appropriate subviews in the appropriate order
+ [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
+ [self.containerView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
+ [self setNeedsDisplay];
+
+ [self addSubview:self.backgroundView];
+ [self addSubview:self.containerView];
+
+ if (self.contentView) {
+ [self.containerView addSubview:self.contentView];
+ }
+ else {
+ if (self.titleViewOrDefault) [self.containerView addSubview:self.titleViewOrDefault];
+ if (self.subtitleViewOrDefault) [self.containerView addSubview:self.subtitleViewOrDefault];
+ }
+ if (self.leftAccessoryView) [self.containerView addSubview:self.leftAccessoryView];
+ if (self.rightAccessoryView) [self.containerView addSubview:self.rightAccessoryView];
+}
+
+// Accessory margins. Accessories are centered vertically when shorter
+// than the callout, otherwise they grow from the upper corner.
+
+- (CGFloat)leftAccessoryVerticalMargin {
+ if (self.leftAccessoryView.frameHeight < self.calloutContainerHeight)
+ return roundf((self.calloutContainerHeight - self.leftAccessoryView.frameHeight) / 2);
+ else
+ return 0;
+}
+
+- (CGFloat)leftAccessoryHorizontalMargin {
+ return fminf(self.leftAccessoryVerticalMargin, TITLE_HMARGIN);
+}
+
+- (CGFloat)rightAccessoryVerticalMargin {
+ if (self.rightAccessoryView.frameHeight < self.calloutContainerHeight)
+ return roundf((self.calloutContainerHeight - self.rightAccessoryView.frameHeight) / 2);
+ else
+ return 0;
+}
+
+- (CGFloat)rightAccessoryHorizontalMargin {
+ return fminf(self.rightAccessoryVerticalMargin, TITLE_HMARGIN);
+}
+
+- (CGFloat)innerContentMarginLeft {
+ if (self.leftAccessoryView)
+ return self.leftAccessoryHorizontalMargin + self.leftAccessoryView.frameWidth + TITLE_HMARGIN;
+ else
+ return self.contentViewInset.left;
+}
+
+- (CGFloat)innerContentMarginRight {
+ if (self.rightAccessoryView)
+ return self.rightAccessoryHorizontalMargin + self.rightAccessoryView.frameWidth + TITLE_HMARGIN;
+ else
+ return self.contentViewInset.right;
+}
+
+- (CGFloat)calloutHeight {
+ return self.calloutContainerHeight + self.backgroundView.anchorHeight;
+}
+
+- (CGFloat)calloutContainerHeight {
+ if (self.contentView)
+ return self.contentView.frameHeight + self.contentViewInset.bottom + self.contentViewInset.top;
+ else if (self.subtitleView || self.subtitle.length > 0)
+ return CALLOUT_SUB_DEFAULT_CONTAINER_HEIGHT;
+ else
+ return CALLOUT_DEFAULT_CONTAINER_HEIGHT;
+}
+
+- (CGSize)sizeThatFits:(CGSize)size {
+
+ // calculate how much non-negotiable space we need to reserve for margin and accessories
+ CGFloat margin = self.innerContentMarginLeft + self.innerContentMarginRight;
+
+ // how much room is left for text?
+ CGFloat availableWidthForText = size.width - margin - 1;
+
+ // no room for text? then we'll have to squeeze into the given size somehow.
+ if (availableWidthForText < 0)
+ availableWidthForText = 0;
+
+ CGSize preferredTitleSize = [self.titleViewOrDefault sizeThatFits:CGSizeMake(availableWidthForText, TITLE_HEIGHT)];
+ CGSize preferredSubtitleSize = [self.subtitleViewOrDefault sizeThatFits:CGSizeMake(availableWidthForText, SUBTITLE_HEIGHT)];
+
+ // total width we'd like
+ CGFloat preferredWidth;
+
+ if (self.contentView) {
+
+ // if we have a content view, then take our preferred size directly from that
+ preferredWidth = self.contentView.frameWidth + margin;
+ }
+ else if (preferredTitleSize.width >= 0.000001 || preferredSubtitleSize.width >= 0.000001) {
+
+ // if we have a title or subtitle, then our assumed margins are valid, and we can apply them
+ preferredWidth = fmaxf(preferredTitleSize.width, preferredSubtitleSize.width) + margin;
+ }
+ else {
+ // ok we have no title or subtitle to speak of. In this case, the system callout would actually not display
+ // at all! But we can handle it.
+ preferredWidth = self.leftAccessoryView.frameWidth + self.rightAccessoryView.frameWidth + self.leftAccessoryHorizontalMargin + self.rightAccessoryHorizontalMargin;
+
+ if (self.leftAccessoryView && self.rightAccessoryView)
+ preferredWidth += BETWEEN_ACCESSORIES_MARGIN;
+ }
+
+ // ensure we're big enough to fit our graphics!
+ preferredWidth = fmaxf(preferredWidth, CALLOUT_MIN_WIDTH);
+
+ // ask to be smaller if we have space, otherwise we'll fit into what we have by truncating the title/subtitle.
+ return CGSizeMake(fminf(preferredWidth, size.width), self.calloutHeight);
+}
+
+- (CGSize)offsetToContainRect:(CGRect)innerRect inRect:(CGRect)outerRect {
+ CGFloat nudgeRight = fmaxf(0, CGRectGetMinX(outerRect) - CGRectGetMinX(innerRect));
+ CGFloat nudgeLeft = fminf(0, CGRectGetMaxX(outerRect) - CGRectGetMaxX(innerRect));
+ CGFloat nudgeTop = fmaxf(0, CGRectGetMinY(outerRect) - CGRectGetMinY(innerRect));
+ CGFloat nudgeBottom = fminf(0, CGRectGetMaxY(outerRect) - CGRectGetMaxY(innerRect));
+ return CGSizeMake(nudgeLeft ? nudgeLeft : nudgeRight, nudgeTop ? nudgeTop : nudgeBottom);
+}
+
+- (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];
+}
+
+- (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer constrainedToLayer:(CALayer *)constrainedLayer animated:(BOOL)animated {
+ [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 {
+
+ // Sanity check: dismiss this callout immediately if it's displayed somewhere
+ if (self.layer.superlayer) [self dismissCalloutAnimated:NO];
+
+ // cancel all animations that may be in progress
+ [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);
+
+ constrainedRect = CGRectInset(constrainedRect, COMFORTABLE_MARGIN, COMFORTABLE_MARGIN);
+
+ // form our subviews based on our content set so far
+ [self rebuildSubviews];
+
+ // apply title/subtitle (if present
+ self.titleLabel.text = self.title;
+ self.subtitleLabel.text = self.subtitle;
+
+ // size the callout to fit the width constraint as best as possible
+ self.frameSize = [self sizeThatFits:CGSizeMake(constrainedRect.size.width, self.calloutHeight)];
+
+ // how much room do we have in the constraint box, both above and below our target rect?
+ CGFloat topSpace = CGRectGetMinY(rect) - CGRectGetMinY(constrainedRect);
+ CGFloat bottomSpace = CGRectGetMaxY(constrainedRect) - CGRectGetMaxY(rect);
+
+ // we prefer to point our arrow down.
+ MGLSMCalloutArrowDirection bestDirection = MGLSMCalloutArrowDirectionDown;
+
+ // we'll point it up though if that's the only option you gave us.
+ if (self.permittedArrowDirection == MGLSMCalloutArrowDirectionUp)
+ bestDirection = MGLSMCalloutArrowDirectionUp;
+
+ // or, if we don't have enough space on the top and have more space on the bottom, and you
+ // gave us a choice, then pointing up is the better option.
+ if (self.permittedArrowDirection == MGLSMCalloutArrowDirectionAny && topSpace < self.calloutHeight && bottomSpace > topSpace)
+ bestDirection = MGLSMCalloutArrowDirectionUp;
+
+ self.currentArrowDirection = bestDirection;
+
+ // we want to point directly at the horizontal center of the given rect. calculate our "anchor point" in terms of our
+ // target view's coordinate system. make sure to offset the anchor point as requested if necessary.
+ CGFloat anchorX = self.calloutOffset.x + CGRectGetMidX(rect);
+ CGFloat anchorY = self.calloutOffset.y + (bestDirection == MGLSMCalloutArrowDirectionDown ? CGRectGetMinY(rect) : CGRectGetMaxY(rect));
+
+ // we prefer to sit centered directly above our anchor
+ CGFloat calloutX = roundf(anchorX - self.frameWidth / 2);
+
+ // but not if it's going to get too close to the edge of our constraints
+ if (calloutX < constrainedRect.origin.x)
+ calloutX = constrainedRect.origin.x;
+
+ if (calloutX > constrainedRect.origin.x+constrainedRect.size.width-self.frameWidth)
+ calloutX = constrainedRect.origin.x+constrainedRect.size.width-self.frameWidth;
+
+ // what's the farthest to the left and right that we could point to, given our background image constraints?
+ CGFloat minPointX = calloutX + self.backgroundView.anchorMargin;
+ CGFloat maxPointX = calloutX + self.frameWidth - self.backgroundView.anchorMargin;
+
+ // we may need to scoot over to the left or right to point at the correct spot
+ CGFloat adjustX = 0;
+ if (anchorX < minPointX) adjustX = anchorX - minPointX;
+ if (anchorX > maxPointX) adjustX = anchorX - maxPointX;
+
+ // add the callout to the given layer (or view if possible, to receive touch events)
+ if (view)
+ [view addSubview:self];
+ else
+ [layer addSublayer:self.layer];
+
+ CGPoint calloutOrigin = {
+ .x = calloutX + adjustX,
+ .y = bestDirection == MGLSMCalloutArrowDirectionDown ? (anchorY - self.calloutHeight) : anchorY
+ };
+
+ self.frameOrigin = calloutOrigin;
+
+ // now set the *actual* anchor point for our layer so that our "popup" animation starts from this point.
+ CGPoint anchorPoint = [layer convertPoint:CGPointMake(anchorX, anchorY) toLayer:self.layer];
+
+ // pass on the anchor point to our background view so it knows where to draw the arrow
+ self.backgroundView.arrowPoint = anchorPoint;
+
+ // adjust it to unit coordinates for the actual layer.anchorPoint property
+ anchorPoint.x /= self.frameWidth;
+ anchorPoint.y /= self.frameHeight;
+ self.layer.anchorPoint = anchorPoint;
+
+ // setting the anchor point moves the view a bit, so we need to reset
+ self.frameOrigin = calloutOrigin;
+
+ // make sure our frame is not on half-pixels or else we may be blurry!
+ CGFloat scale = [UIScreen mainScreen].scale;
+ self.frameX = floorf(self.frameX*scale)/scale;
+ self.frameY = floorf(self.frameY*scale)/scale;
+
+ // layout now so we can immediately start animating to the final position if needed
+ [self setNeedsLayout];
+ [self layoutIfNeeded];
+
+ // if we're outside the bounds of our constraint rect, we'll give our delegate an opportunity to shift us into position.
+ // consider both our size and the size of our target rect (which we'll assume to be the size of the content you want to scroll into view.
+ CGRect contentRect = CGRectUnion(self.frame, rect);
+ CGSize offset = [self offsetToContainRect:contentRect inRect:constrainedRect];
+
+ NSTimeInterval delay = 0;
+ self.popupCancelled = NO; // reset this before calling our delegate below
+
+ if ([self.delegate respondsToSelector:@selector(calloutView:delayForRepositionWithSize:)] && !CGSizeEqualToSize(offset, CGSizeZero))
+ delay = [self.delegate calloutView:(id)self delayForRepositionWithSize:offset];
+
+ // there's a chance that user code in the delegate method may have called -dismissCalloutAnimated to cancel things; if that
+ // happened then we need to bail!
+ if (self.popupCancelled) return;
+
+ // now we want to mask our contents to our background view (if requested) to match the iOS 7 style
+ self.containerView.layer.mask = self.backgroundView.contentMask;
+
+ // if we need to delay, we don't want to be visible while we're delaying, so hide us in preparation for our popup
+ self.hidden = YES;
+
+ // create the appropriate animation, even if we're not animated
+ CAAnimation *animation = [self animationWithType:self.presentAnimation presenting:YES];
+
+ // nuke the duration if no animation requested - we'll still need to "run" the animation to get delays and callbacks
+ if (!animated)
+ animation.duration = 0.0000001; // can't be zero or the animation won't "run"
+
+ animation.beginTime = CACurrentMediaTime() + delay;
+ animation.delegate = self;
+
+ [self.layer addAnimation:animation forKey:@"present"];
+}
+
+- (void)animationDidStart:(CAAnimation *)anim {
+ BOOL presenting = [[anim valueForKey:@"presenting"] boolValue];
+
+ if (presenting) {
+ if ([_delegate respondsToSelector:@selector(calloutViewWillAppear:)])
+ [_delegate calloutViewWillAppear:(id)self];
+
+ // ok, animation is on, let's make ourselves visible!
+ self.hidden = NO;
+ }
+ else if (!presenting) {
+ if ([_delegate respondsToSelector:@selector(calloutViewWillDisappear:)])
+ [_delegate calloutViewWillDisappear:(id)self];
+ }
+}
+
+- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)finished {
+ BOOL presenting = [[anim valueForKey:@"presenting"] boolValue];
+
+ if (presenting && finished) {
+ if ([_delegate respondsToSelector:@selector(calloutViewDidAppear:)])
+ [_delegate calloutViewDidAppear:(id)self];
+ }
+ else if (!presenting && finished) {
+
+ [self removeFromParent];
+ [self.layer removeAnimationForKey:@"dismiss"];
+
+ if ([_delegate respondsToSelector:@selector(calloutViewDidDisappear:)])
+ [_delegate calloutViewDidDisappear:(id)self];
+ }
+}
+
+- (void)dismissCalloutAnimated:(BOOL)animated {
+
+ // cancel all animations that may be in progress
+ [self.layer removeAnimationForKey:@"present"];
+ [self.layer removeAnimationForKey:@"dismiss"];
+
+ self.popupCancelled = YES;
+
+ if (animated) {
+ CAAnimation *animation = [self animationWithType:self.dismissAnimation presenting:NO];
+ animation.delegate = self;
+ [self.layer addAnimation:animation forKey:@"dismiss"];
+ }
+ else {
+ [self removeFromParent];
+ }
+}
+
+- (void)removeFromParent {
+ if (self.superview)
+ [self removeFromSuperview];
+ else {
+ // removing a layer from a superlayer causes an implicit fade-out animation that we wish to disable.
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+ [self.layer removeFromSuperlayer];
+ [CATransaction commit];
+ }
+}
+
+- (CAAnimation *)animationWithType:(MGLSMCalloutAnimation)type presenting:(BOOL)presenting {
+ CAAnimation *animation = nil;
+
+ if (type == MGLSMCalloutAnimationBounce) {
+
+ CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"opacity"];
+ fade.duration = 0.23;
+ fade.fromValue = presenting ? @0.0 : @1.0;
+ fade.toValue = presenting ? @1.0 : @0.0;
+ fade.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+
+ CABasicAnimation *bounce = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
+ bounce.duration = 0.23;
+ bounce.fromValue = presenting ? @0.7 : @1.0;
+ bounce.toValue = presenting ? @1.0 : @0.7;
+ bounce.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.59367:0.12066:0.18878:1.5814];
+
+ CAAnimationGroup *group = [CAAnimationGroup animation];
+ group.animations = @[fade, bounce];
+ group.duration = 0.23;
+
+ animation = group;
+ }
+ else if (type == MGLSMCalloutAnimationFade) {
+ CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"opacity"];
+ fade.duration = 1.0/3.0;
+ fade.fromValue = presenting ? @0.0 : @1.0;
+ fade.toValue = presenting ? @1.0 : @0.0;
+ animation = fade;
+ }
+ else if (type == MGLSMCalloutAnimationStretch) {
+ CABasicAnimation *stretch = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
+ stretch.duration = 0.1;
+ stretch.fromValue = presenting ? @0.0 : @1.0;
+ stretch.toValue = presenting ? @1.0 : @0.0;
+ animation = stretch;
+ }
+
+ // CAAnimation is KVC compliant, so we can store whether we're presenting for lookup in our delegate methods
+ [animation setValue:@(presenting) forKey:@"presenting"];
+
+ animation.fillMode = kCAFillModeForwards;
+ animation.removedOnCompletion = NO;
+ return animation;
+}
+
+- (void)layoutSubviews {
+
+ self.containerView.frame = self.bounds;
+ self.backgroundView.frame = self.bounds;
+
+ // if we're pointing up, we'll need to push almost everything down a bit
+ CGFloat dy = self.currentArrowDirection == MGLSMCalloutArrowDirectionUp ? TOP_ANCHOR_MARGIN : 0;
+
+ self.titleViewOrDefault.frameX = self.innerContentMarginLeft;
+ self.titleViewOrDefault.frameY = (self.subtitleView || self.subtitle.length ? TITLE_SUB_TOP : TITLE_TOP) + dy;
+ self.titleViewOrDefault.frameWidth = self.frameWidth - self.innerContentMarginLeft - self.innerContentMarginRight;
+
+ self.subtitleViewOrDefault.frameX = self.titleViewOrDefault.frameX;
+ self.subtitleViewOrDefault.frameY = SUBTITLE_TOP + dy;
+ self.subtitleViewOrDefault.frameWidth = self.titleViewOrDefault.frameWidth;
+
+ self.leftAccessoryView.frameX = self.leftAccessoryHorizontalMargin;
+ self.leftAccessoryView.frameY = self.leftAccessoryVerticalMargin + dy;
+
+ self.rightAccessoryView.frameX = self.frameWidth - self.rightAccessoryHorizontalMargin - self.rightAccessoryView.frameWidth;
+ self.rightAccessoryView.frameY = self.rightAccessoryVerticalMargin + dy;
+
+ if (self.contentView) {
+ self.contentView.frameX = self.innerContentMarginLeft;
+ self.contentView.frameY = self.contentViewInset.top + dy;
+ }
+}
+
+#pragma mark - Accessibility
+
+- (NSInteger)accessibilityElementCount {
+ return (!!self.leftAccessoryView + !!self.titleViewOrDefault +
+ !!self.subtitleViewOrDefault + !!self.rightAccessoryView);
+}
+
+- (id)accessibilityElementAtIndex:(NSInteger)index {
+ if (index == 0) {
+ return self.leftAccessoryView ? self.leftAccessoryView : self.titleViewOrDefault;
+ }
+ if (index == 1) {
+ return self.leftAccessoryView ? self.titleViewOrDefault : self.subtitleViewOrDefault;
+ }
+ if (index == 2) {
+ return self.leftAccessoryView ? self.subtitleViewOrDefault : self.rightAccessoryView;
+ }
+ if (index == 3) {
+ return self.leftAccessoryView ? self.rightAccessoryView : nil;
+ }
+ return nil;
+}
+
+- (NSInteger)indexOfAccessibilityElement:(id)element {
+ if (element == nil) return NSNotFound;
+ if (element == self.leftAccessoryView) return 0;
+ if (element == self.titleViewOrDefault) {
+ return self.leftAccessoryView ? 1 : 0;
+ }
+ if (element == self.subtitleViewOrDefault) {
+ return self.leftAccessoryView ? 2 : 1;
+ }
+ if (element == self.rightAccessoryView) {
+ return self.leftAccessoryView ? 3 : 2;
+ }
+ return NSNotFound;
+}
+
+@end
+
+// import this known "private API" from SMCalloutBackgroundView
+@interface MGLSMCalloutBackgroundView (EmbeddedImages)
++ (UIImage *)embeddedImageNamed:(NSString *)name;
+@end
+
+//
+// Callout Background View.
+//
+
+@interface MGLSMCalloutMaskedBackgroundView ()
+@property (nonatomic, strong) UIView *containerView, *containerBorderView, *arrowView;
+@property (nonatomic, strong) UIImageView *arrowImageView, *arrowHighlightedImageView, *arrowBorderView;
+@end
+
+static UIImage *blackArrowImage = nil, *whiteArrowImage = nil, *grayArrowImage = nil;
+
+@implementation MGLSMCalloutMaskedBackgroundView
+
+- (id)initWithFrame:(CGRect)frame {
+ if (self = [super initWithFrame:frame]) {
+
+ // Here we're mimicking the very particular (and odd) structure of the system callout view.
+ // The hierarchy and view/layer values were discovered by inspecting map kit using Reveal.app
+
+ self.containerView = [UIView new];
+ self.containerView.backgroundColor = [UIColor whiteColor];
+ self.containerView.alpha = 0.96;
+ self.containerView.layer.cornerRadius = 8;
+ self.containerView.layer.shadowRadius = 30;
+ self.containerView.layer.shadowOpacity = 0.1;
+
+ self.containerBorderView = [UIView new];
+ self.containerBorderView.layer.borderColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
+ self.containerBorderView.layer.borderWidth = 0.5;
+ self.containerBorderView.layer.cornerRadius = 8.5;
+
+ if (!blackArrowImage) {
+ blackArrowImage = [MGLSMCalloutBackgroundView embeddedImageNamed:@"CalloutArrow"];
+ whiteArrowImage = [self image:blackArrowImage withColor:[UIColor whiteColor]];
+ grayArrowImage = [self image:blackArrowImage withColor:[UIColor colorWithWhite:0.85 alpha:1]];
+ }
+
+ self.anchorHeight = 13;
+ self.anchorMargin = 27;
+
+ self.arrowView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, blackArrowImage.size.width, blackArrowImage.size.height)];
+ self.arrowView.alpha = 0.96;
+ self.arrowImageView = [[UIImageView alloc] initWithImage:whiteArrowImage];
+ self.arrowHighlightedImageView = [[UIImageView alloc] initWithImage:grayArrowImage];
+ self.arrowHighlightedImageView.hidden = YES;
+ self.arrowBorderView = [[UIImageView alloc] initWithImage:blackArrowImage];
+ self.arrowBorderView.alpha = 0.1;
+ self.arrowBorderView.frameY = 0.5;
+
+ [self addSubview:self.containerView];
+ [self.containerView addSubview:self.containerBorderView];
+ [self addSubview:self.arrowView];
+ [self.arrowView addSubview:self.arrowBorderView];
+ [self.arrowView addSubview:self.arrowImageView];
+ [self.arrowView addSubview:self.arrowHighlightedImageView];
+ }
+ return self;
+}
+
+// Make sure we relayout our images when our arrow point changes!
+- (void)setArrowPoint:(CGPoint)arrowPoint {
+ [super setArrowPoint:arrowPoint];
+ [self setNeedsLayout];
+}
+
+- (void)setHighlighted:(BOOL)highlighted {
+ [super setHighlighted:highlighted];
+ self.containerView.backgroundColor = highlighted ? [UIColor colorWithWhite:0.85 alpha:1] : [UIColor whiteColor];
+ self.arrowImageView.hidden = highlighted;
+ self.arrowHighlightedImageView.hidden = !highlighted;
+}
+
+- (UIImage *)image:(UIImage *)image withColor:(UIColor *)color {
+
+ UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
+ CGRect imageRect = (CGRect){.size=image.size};
+ CGContextRef c = UIGraphicsGetCurrentContext();
+ CGContextTranslateCTM(c, 0, image.size.height);
+ CGContextScaleCTM(c, 1, -1);
+ CGContextClipToMask(c, imageRect, image.CGImage);
+ [color setFill];
+ CGContextFillRect(c, imageRect);
+ UIImage *whiteImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+ return whiteImage;
+}
+
+- (void)layoutSubviews {
+
+ BOOL pointingUp = self.arrowPoint.y < self.frameHeight/2;
+
+ // if we're pointing up, we'll need to push almost everything down a bit
+ CGFloat dy = pointingUp ? TOP_ANCHOR_MARGIN : 0;
+
+ self.containerView.frame = CGRectMake(0, dy, self.frameWidth, self.frameHeight - self.arrowView.frameHeight + 0.5);
+ self.containerBorderView.frame = CGRectInset(self.containerView.bounds, -0.5, -0.5);
+
+ self.arrowView.frameX = roundf(self.arrowPoint.x - self.arrowView.frameWidth / 2);
+
+ if (pointingUp) {
+ self.arrowView.frameY = 1;
+ self.arrowView.transform = CGAffineTransformMakeRotation(M_PI);
+ }
+ else {
+ self.arrowView.frameY = self.containerView.frameHeight - 0.5;
+ self.arrowView.transform = CGAffineTransformIdentity;
+ }
+}
+
+- (CALayer *)contentMask {
+
+ UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
+
+ [self.layer renderInContext:UIGraphicsGetCurrentContext()];
+
+ UIImage *maskImage = UIGraphicsGetImageFromCurrentImageContext();
+ UIGraphicsEndImageContext();
+
+ CALayer *layer = [CALayer layer];
+ layer.frame = self.bounds;
+ layer.contents = (id)maskImage.CGImage;
+ return layer;
+}
+
+@end
+
+@implementation MGLSMCalloutBackgroundView
+
++ (NSData *)dataWithBase64EncodedString:(NSString *)string {
+ //
+ // NSData+Base64.m
+ //
+ // Version 1.0.2
+ //
+ // Created by Nick Lockwood on 12/01/2012.
+ // Copyright (C) 2012 Charcoal Design
+ //
+ // Distributed under the permissive zlib License
+ // Get the latest version from here:
+ //
+ // https://github.com/nicklockwood/Base64
+ //
+ // This software is provided 'as-is', without any express or implied
+ // warranty. In no event will the authors be held liable for any damages
+ // arising from the use of this software.
+ //
+ // Permission is granted to anyone to use this software for any purpose,
+ // including commercial applications, and to alter it and redistribute it
+ // freely, subject to the following restrictions:
+ //
+ // 1. The origin of this software must not be misrepresented; you must not
+ // claim that you wrote the original software. If you use this software
+ // in a product, an acknowledgment in the product documentation would be
+ // appreciated but is not required.
+ //
+ // 2. Altered source versions must be plainly marked as such, and must not be
+ // misrepresented as being the original software.
+ //
+ // 3. This notice may not be removed or altered from any source distribution.
+ //
+ const char lookup[] = {
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 99, 99, 99,
+ 99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99,
+ 99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 99, 99, 99, 99, 99
+ };
+
+ NSData *inputData = [string dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
+ long long inputLength = [inputData length];
+ const unsigned char *inputBytes = [inputData bytes];
+
+ long long maxOutputLength = (inputLength / 4 + 1) * 3;
+ NSMutableData *outputData = [NSMutableData dataWithLength:(NSUInteger)maxOutputLength];
+ unsigned char *outputBytes = (unsigned char *)[outputData mutableBytes];
+
+ int accumulator = 0;
+ long long outputLength = 0;
+ unsigned char accumulated[] = {0, 0, 0, 0};
+ for (long long i = 0; i < inputLength; i++) {
+ unsigned char decoded = lookup[inputBytes[i] & 0x7F];
+ if (decoded != 99) {
+ accumulated[accumulator] = decoded;
+ if (accumulator == 3) {
+ outputBytes[outputLength++] = (accumulated[0] << 2) | (accumulated[1] >> 4);
+ outputBytes[outputLength++] = (accumulated[1] << 4) | (accumulated[2] >> 2);
+ outputBytes[outputLength++] = (accumulated[2] << 6) | accumulated[3];
+ }
+ accumulator = (accumulator + 1) % 4;
+ }
+ }
+
+ //handle left-over data
+ if (accumulator > 0) outputBytes[outputLength] = (accumulated[0] << 2) | (accumulated[1] >> 4);
+ if (accumulator > 1) outputBytes[++outputLength] = (accumulated[1] << 4) | (accumulated[2] >> 2);
+ if (accumulator > 2) outputLength++;
+
+ //truncate data to match actual output length
+ outputData.length = (NSUInteger)outputLength;
+ return outputLength? outputData: nil;
+}
+
++ (UIImage *)embeddedImageNamed:(NSString *)name {
+ CGFloat screenScale = [UIScreen mainScreen].scale;
+ if (screenScale > 1.0) {
+ name = [name stringByAppendingString:@"_2x"];
+ screenScale = 2.0;
+ }
+
+ SEL selector = NSSelectorFromString(name);
+
+ if (![(id)self respondsToSelector:selector]) {
+ NSLog(@"Could not find an embedded image. Ensure that you've added a class-level method named +%@", name);
+ return nil;
+ }
+
+ // We need to hush the compiler here - but we know what we're doing!
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ NSString *base64String = [(id)self performSelector:selector];
+ #pragma clang diagnostic pop
+
+ UIImage *rawImage = [UIImage imageWithData:[self dataWithBase64EncodedString:base64String]];
+ return [UIImage imageWithCGImage:rawImage.CGImage scale:screenScale orientation:UIImageOrientationUp];
+}
+
++ (NSString *)CalloutArrow { return @"iVBORw0KGgoAAAANSUhEUgAAACcAAAANCAYAAAAqlHdlAAAAHGlET1QAAAACAAAAAAAAAAcAAAAoAAAABwAAAAYAAADJEgYpIwAAAJVJREFUOBFiYIAAdn5+fkFOTkE5Dg5eW05O3lJOTr6zQPyfDhhoD28pxF5BOZA7gE5ih7oLN8XJyR8MdNwrGjkQaC5/MG7biZDh4OBXBDruLpUdeBdkLhHWE1bCzs6nAnTcUyo58DnIPMK2kqAC6DALIP5JoQNB+i1IsJZ4pcBEm0iJ40D6ibeNDJVAx00k04ETSbUOAAAA//+SwicfAAAAe0lEQVRjYCAdMHNy8u7l5OT7Tzzm3Qu0hpl0q8jQwcPDIwp02B0iHXeHl5dXhAxryNfCzc2tC3TcJwIO/ARSR74tFOjk4uL1BzruHw4H/gPJU2A85Vq5uPjTgY77g+bAPyBxyk2nggkcHPxOnJz8B4AOfAGiQXwqGMsAACGK1kPPMHNBAAAAAElFTkSuQmCC"; }
+
++ (NSString *)CalloutArrow_2x { return @"iVBORw0KGgoAAAANSUhEUgAAAE4AAAAaCAYAAAAZtWr8AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAHGlET1QAAAACAAAAAAAAAA0AAAAoAAAADQAAAA0AAAFMRh0LGwAAARhJREFUWAnclbENwjAQRZ0mih2fDYgsQEVDxQZMgKjpWYAJkBANI8AGDIEoM0WkzBDRAf8klB44g0OkU1zE3/+9RIpS7VVY730/y/woTWlsjJ9iPcN9pbXfY85auyvm/qcDNmb0e2Z+sk/ZBTthN0oVttX12mJIWeaWEFf+kbySmZQa0msu3nzaGJprTXV3BVLNDG/if7bNOTeAvFP35NGJu39GL7Abb27bFXncVQBZLgJf3jp+ebSWIxZMgrxdvPJoJ4gqHpXgV36ITR46HUGaiNMKB6YQd4lI3gV8qTBjmDhrbQFxVQTyKu4ShjJQap7nE4hrfiiv4Q6B8MLGat1bQNztB/JwZm8Rli5wujFu821xfGZgLPUAAAD//4wvm4gAAAD7SURBVOWXMQ6CMBiFgaFpi6VyBEedXJy4hMQTeBSvRDgJEySegI3EQWOivkZnqUB/k0LyL7R9L++D9G+DwP0TCZGUqCdRlYgUuY9F4JCmqQa0hgBcY7wIItFZMLZYS5l0ruAZbXhs6BIROgmhcoB7OIAHTZUTRqG3wp9xmhqc0aRPQu8YAlwxIbwCEUL6GH9wfDcLXY2HpyvvmkHf9+BcrwCuHQGvNRp9Pl6OY0PPAO42AB7WqMxLKLahpFR7gLv/AA9zPe+gtvAMCIC7WMC7CqEPtrqzmBfHyy3A1V/g1Th27GYBY0BIxrk6Ap65254/VZp30GID9JwteQEZrVMWXqGn8gAAAABJRU5ErkJggg=="; }
+
+@end
+
+//
+// Our UIView frame helpers implementation
+//
+
+@implementation UIView (SMFrameAdditions)
+
+- (CGPoint)frameOrigin { return self.frame.origin; }
+- (void)setFrameOrigin:(CGPoint)origin { self.frame = (CGRect){ .origin=origin, .size=self.frame.size }; }
+
+- (CGFloat)frameX { return self.frame.origin.x; }
+- (void)setFrameX:(CGFloat)x { self.frame = (CGRect){ .origin.x=x, .origin.y=self.frame.origin.y, .size=self.frame.size }; }
+
+- (CGFloat)frameY { return self.frame.origin.y; }
+- (void)setFrameY:(CGFloat)y { self.frame = (CGRect){ .origin.x=self.frame.origin.x, .origin.y=y, .size=self.frame.size }; }
+
+- (CGSize)frameSize { return self.frame.size; }
+- (void)setFrameSize:(CGSize)size { self.frame = (CGRect){ .origin=self.frame.origin, .size=size }; }
+
+- (CGFloat)frameWidth { return self.frame.size.width; }
+- (void)setFrameWidth:(CGFloat)width { self.frame = (CGRect){ .origin=self.frame.origin, .size.width=width, .size.height=self.frame.size.height }; }
+
+- (CGFloat)frameHeight { return self.frame.size.height; }
+- (void)setFrameHeight:(CGFloat)height { self.frame = (CGRect){ .origin=self.frame.origin, .size.width=self.frame.size.width, .size.height=height }; }
+
+- (CGFloat)frameLeft { return self.frame.origin.x; }
+- (void)setFrameLeft:(CGFloat)left { self.frame = (CGRect){ .origin.x=left, .origin.y=self.frame.origin.y, .size.width=fmaxf(self.frame.origin.x+self.frame.size.width-left,0), .size.height=self.frame.size.height }; }
+
+- (CGFloat)frameTop { return self.frame.origin.y; }
+- (void)setFrameTop:(CGFloat)top { self.frame = (CGRect){ .origin.x=self.frame.origin.x, .origin.y=top, .size.width=self.frame.size.width, .size.height=fmaxf(self.frame.origin.y+self.frame.size.height-top,0) }; }
+
+- (CGFloat)frameRight { return self.frame.origin.x + self.frame.size.width; }
+- (void)setFrameRight:(CGFloat)right { self.frame = (CGRect){ .origin=self.frame.origin, .size.width=fmaxf(right-self.frame.origin.x,0), .size.height=self.frame.size.height }; }
+
+- (CGFloat)frameBottom { return self.frame.origin.y + self.frame.size.height; }
+- (void)setFrameBottom:(CGFloat)bottom { self.frame = (CGRect){ .origin=self.frame.origin, .size.width=self.frame.size.width, .size.height=fmaxf(bottom-self.frame.origin.y,0) }; }
+
+@end
diff --git a/platform/linux/README.md b/platform/linux/README.md
index b6a3b9a446..8b8ac9d089 100644
--- a/platform/linux/README.md
+++ b/platform/linux/README.md
@@ -8,12 +8,15 @@ This process gives you a Linux desktop app built on a Linux host system.
### Build
-Install GCC 5+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/).
+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/).
sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
sudo apt-get update
- sudo apt-get install gcc-5 g++-5
- export CXX=g++-5
+ 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:
diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake
index 41e7f71b99..dd5f0af112 100644
--- a/platform/linux/config.cmake
+++ b/platform/linux/config.cmake
@@ -1,6 +1,5 @@
-mason_use(glfw VERSION 2017-02-09-77a8f10)
+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)
@@ -8,12 +7,11 @@ mason_use(libpng VERSION 1.6.25)
mason_use(libjpeg-turbo VERSION 1.5.0)
mason_use(webp VERSION 0.5.1)
mason_use(gtest VERSION 1.8.0${MASON_CXXABI_SUFFIX})
-mason_use(benchmark VERSION 1.0.0-1)
+mason_use(benchmark VERSION 1.2.0)
mason_use(icu VERSION 58.1-min-size)
+mason_use(args VERSION 6.2.0 HEADER_ONLY)
-# Link with libuv. This is not part of loop-uv.cmake because loop-uv.cmake is also
-# used by node.cmake, where we want to link with the libuv provided by node itself.
-target_add_mason_package(mbgl-loop-uv PUBLIC libuv)
+include(cmake/loop-uv.cmake)
macro(mbgl_platform_core)
target_add_mason_package(mbgl-core PUBLIC mesa)
@@ -21,7 +19,6 @@ macro(mbgl_platform_core)
if(WITH_OSMESA)
target_sources(mbgl-core
PRIVATE platform/default/headless_backend_osmesa.cpp
- PRIVATE platform/default/mbgl/gl/headless_display.cpp
)
target_link_libraries(mbgl-core
PUBLIC -lOSMesa
@@ -29,17 +26,14 @@ macro(mbgl_platform_core)
elseif(WITH_EGL)
target_sources(mbgl-core
PRIVATE platform/linux/src/headless_backend_egl.cpp
- PRIVATE platform/linux/src/headless_display_egl.cpp
)
target_link_libraries(mbgl-core
PUBLIC -lGLESv2
PUBLIC -lEGL
- PUBLIC -lgbm
)
else()
target_sources(mbgl-core
PRIVATE platform/linux/src/headless_backend_glx.cpp
- PRIVATE platform/linux/src/headless_display_glx.cpp
)
target_link_libraries(mbgl-core
PUBLIC -lGL
@@ -48,27 +42,13 @@ macro(mbgl_platform_core)
endif()
target_sources(mbgl-core
- # File source
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/http_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
- PRIVATE platform/default/sqlite3.hpp
-
# Misc
PRIVATE platform/default/logging_stderr.cpp
PRIVATE platform/default/string_stdlib.cpp
PRIVATE platform/default/thread.cpp
PRIVATE platform/default/bidi.cpp
+ PRIVATE platform/default/local_glyph_rasterizer.cpp
+ PRIVATE platform/default/thread_local.cpp
PRIVATE platform/default/utf.cpp
# Image handling
@@ -79,11 +59,10 @@ macro(mbgl_platform_core)
PRIVATE platform/default/webp_reader.cpp
# Headless view
+ PRIVATE platform/default/mbgl/gl/headless_frontend.cpp
+ 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
- PRIVATE platform/default/mbgl/gl/offscreen_view.cpp
- PRIVATE platform/default/mbgl/gl/offscreen_view.hpp
# Thread pool
PRIVATE platform/default/mbgl/util/default_thread_pool.cpp
@@ -93,9 +72,9 @@ macro(mbgl_platform_core)
target_include_directories(mbgl-core
PRIVATE platform/default
+ PRIVATE platform/linux
)
- target_add_mason_package(mbgl-core PUBLIC sqlite)
target_add_mason_package(mbgl-core PUBLIC nunicode)
target_add_mason_package(mbgl-core PUBLIC libpng)
target_add_mason_package(mbgl-core PUBLIC libjpeg-turbo)
@@ -104,6 +83,22 @@ macro(mbgl_platform_core)
target_link_libraries(mbgl-core
PUBLIC -lz
+ )
+endmacro()
+
+
+macro(mbgl_filesource)
+ target_sources(mbgl-filesource
+ # File source
+ PRIVATE platform/default/http_file_source.cpp
+
+ # Database
+ PRIVATE platform/default/sqlite3.cpp
+ )
+
+ target_add_mason_package(mbgl-filesource PUBLIC sqlite)
+
+ target_link_libraries(mbgl-filesource
PUBLIC -lcurl
)
endmacro()
@@ -111,13 +106,16 @@ endmacro()
macro(mbgl_platform_glfw)
target_link_libraries(mbgl-glfw
+ PRIVATE mbgl-filesource
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()
@@ -125,15 +123,21 @@ endmacro()
macro(mbgl_platform_render)
target_link_libraries(mbgl-render
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
+
+ target_add_mason_package(mbgl-render PUBLIC libuv)
endmacro()
macro(mbgl_platform_offline)
target_link_libraries(mbgl-offline
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
+
+ target_add_mason_package(mbgl-offline PUBLIC libuv)
endmacro()
@@ -142,6 +146,10 @@ macro(mbgl_platform_test)
PRIVATE platform/default/mbgl/test/main.cpp
)
+ target_include_directories(mbgl-test
+ PRIVATE platform/linux
+ )
+
set_source_files_properties(
platform/default/mbgl/test/main.cpp
PROPERTIES
@@ -149,8 +157,11 @@ macro(mbgl_platform_test)
)
target_link_libraries(mbgl-test
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
+
+ target_add_mason_package(mbgl-test PUBLIC libuv)
endmacro()
@@ -166,8 +177,11 @@ macro(mbgl_platform_benchmark)
)
target_link_libraries(mbgl-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 d98b2edc03..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.reset(new HeadlessDisplay);
- }
- 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 eec0e7656f..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.reset(new HeadlessDisplay);
+ // 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 03c8e16a59..0000000000
--- a/platform/linux/src/headless_display_egl.cpp
+++ /dev/null
@@ -1,65 +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[] = {
- 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 b4766eb45a..4ea1eafb89 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -2,19 +2,73 @@
## master
+* Renamed this SDK from Mapbox macOS SDK to Mapbox Maps SDK for macOS. ([#10610](https://github.com/mapbox/mapbox-gl-native/pull/10610))
+
+### Annotations and user interaction
+
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+
### Styles
-* Added suuport for diplaying georeferenced images via the `MGLImageSource`. [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110)
-### Packaging
+* Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270))
+* Added `MGLComputedShapeSource` source class that allows applications to supply vector data on a per-tile basis.
-* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775))
-* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771), [#8773](https://github.com/mapbox/mapbox-gl-native/pull/8773))
+## v0.6.0
+
+### Networking and storage
+
+* Added a new `MGLMapSnapshotter` class for capturing rendered map images from an `MGLMapView`’s camera. ([#9891](https://github.com/mapbox/mapbox-gl-native/pull/9891))
+* Reduced the time it takes to create new `MGLMapView` instances in some cases. ([#9864](https://github.com/mapbox/mapbox-gl-native/pull/9864))
+* Added support for forced cache revalidation that will eliminate flickering that was sometimes visible for certain types of tiles (e.g., traffic tiles). ([#9670](https://github.com/mapbox/mapbox-gl-native/pull/9670), [#9103](https://github.com/mapbox/mapbox-gl-native/issues/9103))
+* Improved the performance of the SDK when parsing vector tile data used to render the map. ([#9312](https://github.com/mapbox/mapbox-gl-native/pull/9312))
-* The previously-deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+### Styles
+
+* Added a new type of source, represented by the `MGLImageSource` class at runtime, that displays a georeferenced image. ([#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110))
+* Setting a style using `MGLMapView`'s `styleURL` property now smoothly transitions from the previous style to the new style and maintains equivalent layers and sources along with their identifiers. ([#9256](https://github.com/mapbox/mapbox-gl-native/pull/9256))
+* Added `MGLCircleStyleLayer.circlePitchAlignment` and `MGLSymbolStyleLayer.iconPitchAlignment` properties to control whether circles and symbols lie flat against a tilted map. ([#9426](https://github.com/mapbox/mapbox-gl-native/pull/9426), [#9479](https://github.com/mapbox/mapbox-gl-native/pull/9479))
+* Added an `MGLSymbolStyleLayer.iconAnchor` property to control where an icon is anchored. ([#9849](https://github.com/mapbox/mapbox-gl-native/pull/9849))
+* The `maximumTextWidth` and `textLetterSpacing` properties of `MGLSymbolStyleLayer` are now compatible with `MGLSourceStyleFunction`s and `MGLCompositeStyleFunction`s, allowing data-driven styling of these properties. ([#9870](https://github.com/mapbox/mapbox-gl-native/pull/9870))
+* Improved the legibility of labels that follow lines when the map is tilted. ([#9009](https://github.com/mapbox/mapbox-gl-native/pull/9009))
+* Fixed an issue that could cause flickering when a translucent raster style layer was present. ([#9468](https://github.com/mapbox/mapbox-gl-native/pull/9468))
+* Fixed an issue that could cause antialiasing between polygons on the same layer to fail if the fill layers used data-driven styling for the fill color. ([#9699](https://github.com/mapbox/mapbox-gl-native/pull/9699))
+* The previously deprecated support for style classes has been removed. For interface compatibility, the API methods remain, but they are now non-functional.
+
+### Annotations and user interaction
+
+* Fixed several bugs and performance issues related to the use of annotations backed by `MGLAnnotationImage`s. The limits on the number and size of images and glyphs has been effectively eliminated and should now depend on hardware constraints. These fixes also apply to images used to represent icons in `MGLSymbolStyleLayer`s. ([#9213](https://github.com/mapbox/mapbox-gl-native/pull/9213))
+* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
* Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617))
+* Added `-[MGLMapView cameraThatFitsShape:direction:edgePadding:]` 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))
+* Added support selection of shape and polyline annotations.([#9984](https://github.com/mapbox/mapbox-gl-native/pull/9984))
+* Fixed an issue where a shape annotation callout was not displayed if the centroid was not visible. ([#10255](https://github.com/mapbox/mapbox-gl-native/pull/10255))
+
+### Other changes
+
+* Added a Bulgarian localization. ([#10309](https://github.com/mapbox/mapbox-gl-native/pull/10309))
+* Fixed an issue that could cause line label rendering glitches when the line geometry is projected to a point behind the plane of the camera. ([#9865](https://github.com/mapbox/mapbox-gl-native/pull/9865))
+* Fixed an issue that could cause a crash when using `-[MGLMapView flyToCamera:completionHandler:]` and related methods with zoom levels at or near the maximum value. ([#9381](https://github.com/mapbox/mapbox-gl-native/pull/9381))
+
+## 0.5.1
+
+This version of the Mapbox macOS SDK corresponds to version 3.6.4 of the Mapbox iOS SDK.
+
+* Added an `MGLStyle.localizesLabels` property, off by default, that localizes any Mapbox Streets–sourced symbol layer into the user’s preferred language. ([#9582](https://github.com/mapbox/mapbox-gl-native/pull/9582))
+* Fixed an issue that caused `-[MGLShapeSource featuresMatchingPredicate:]` and `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` to always return an empty array. ([#9784](https://github.com/mapbox/mapbox-gl-native/pull/9784))
+* `MGLMapView`’s `minimumZoomLevel` and `maximumZoomLevel` properties are now available in Interface Builder’s Attributes inspector. ([#9729](https://github.com/mapbox/mapbox-gl-native/pull/9729))
+* Added a Hungarian localization. ([#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945))
+* Deprecated `+[MGLStyle trafficDayStyleURL]` and `+[MGLStyle trafficNightStyleURL]` with no replacement method. To use the Traffic Day and Traffic Night styles going forward, we recommend that you use the underlying URL. ([#9918](https://github.com/mapbox/mapbox-gl-native/pull/9918))
+* Fixed an issue where stale (but still valid) map data could be ignored in offline mode. ([#10012](https://github.com/mapbox/mapbox-gl-native/pull/10012))
## 0.5.0
+This version of the Mapbox macOS SDK corresponds to version 3.6.0 of the Mapbox iOS SDK.
+
+### Packaging
+
+* Xcode 8.0 or higher is now recommended for using this SDK. ([#8775](https://github.com/mapbox/mapbox-gl-native/pull/8775))
+* Updated MGLMapView’s logo view to display [the new Mapbox logo](https://www.mapbox.com/blog/new-mapbox-logo/). ([#8771](https://github.com/mapbox/mapbox-gl-native/pull/8771), [#8773](https://github.com/mapbox/mapbox-gl-native/pull/8773))
+
### Styles
* Added support for 3D extrusion of buildings and other polygonal features via the `MGLFillExtrusionStyleLayer` class and the `fill-extrusion` layer type in style JSON. ([#8431](https://github.com/mapbox/mapbox-gl-native/pull/8431))
@@ -25,19 +79,32 @@
* Fixed an issue preventing programmatically added style layers from appearing in already cached tiles. ([#8954](https://github.com/mapbox/mapbox-gl-native/pull/8954))
* Fixed an issue causing a composite function’s highest zoom level stop to be misinterpreted. ([#8613](https://github.com/mapbox/mapbox-gl-native/pull/8613), [#8790](https://github.com/mapbox/mapbox-gl-native/pull/8790))
* Fixed an issue where re-adding a layer that had been previously removed from a style would reset its paint properties. Moved initializers for `MGLTileSource`, `MGLStyleLayer`, and `MGLForegroundStyleLayer` to their concrete subclasses; because these classes were already intended for initialization only via concrete subclasses, this should have no developer impact. ([#8626](https://github.com/mapbox/mapbox-gl-native/pull/8626))
+* Fixed a crash that occurred when removing a source that was still being used by one or more style layers. Since this is a programming error, a warning is logged to the console instead. ([#9129](https://github.com/mapbox/mapbox-gl-native/pull/9129))
* Feature querying results now account for any changes to a feature’s size caused by a source or composite style function. ([#8665](https://github.com/mapbox/mapbox-gl-native/pull/8665))
+* Fixed the behavior of composite functions that specify fractional zoom level stops. ([#9289](https://github.com/mapbox/mapbox-gl-native/pull/9289))
* Letter spacing is now disabled in Arabic text so that ligatures are drawn correctly. ([#9062](https://github.com/mapbox/mapbox-gl-native/pull/9062))
+* Improved the performance of styles using source and composite style functions. ([#9185](https://github.com/mapbox/mapbox-gl-native/pull/9185), [#9257](https://github.com/mapbox/mapbox-gl-native/pull/9257))
+
+### Annotations
+
+* The default marker image has been made slightly larger and now matches the version in the Mapbox iOS SDK. ([#9370](https://github.com/mapbox/mapbox-gl-native/pull/9370))
+* The `MGLPolyline.coordinate` and `MGLPolygon.coordinate` properties now return the midpoint and centroid, respectively, instead of the first coordinate. ([#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713))
+
+### User interaction
+
+* Fixed an issue causing the map to go blank during a flight animation that travels a very short distance. ([#9199](https://github.com/mapbox/mapbox-gl-native/pull/9199))
+* Fixed an issue causing the mouse cursor to jump after Shift- or Option-dragging the map. ([#9390](https://github.com/mapbox/mapbox-gl-native/pull/9390))
+* The Improve This Map button in the attribution action sheet now leads to a feedback tool that matches MGLMapView’s rotation and pitch. `-[MGLAttributionInfo feedbackURLAtCenterCoordinate:zoomLevel:]` no longer respects the feedback URL specified in TileJSON. ([#9078](https://github.com/mapbox/mapbox-gl-native/pull/9078))
### Other changes
* Fixed a crash when calling `MGLMultiPolygon.coordinate` [#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713)
* Fixed an issue causing attribution button text to appear blue instead of black. ([#8701](https://github.com/mapbox/mapbox-gl-native/pull/8701))
* Fixed a crash or console spew when MGLMapView is initialized with a frame smaller than 64 points wide by 64 points tall. ([#8562](https://github.com/mapbox/mapbox-gl-native/pull/8562))
-* The Improve This Map button in the attribution action sheet now leads to a feedback tool that matches MGLMapView’s rotation and pitch. `-[MGLAttributionInfo feedbackURLAtCenterCoordinate:zoomLevel:]` no longer respects the feedback URL specified in TileJSON. ([#9078](https://github.com/mapbox/mapbox-gl-native/pull/9078))
* The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418))
-* The `MGLPolyline.coordinate` and `MGLPolygon.coordinate` properties now return the midpoint and centroid, respectively, instead of the first coordinate. ([#8713](https://github.com/mapbox/mapbox-gl-native/pull/8713))
* Improved CPU and battery performance while animating a tilted map’s camera in an area with many labels. ([#9031](https://github.com/mapbox/mapbox-gl-native/pull/9031))
* Fixed an issue rendering polylines that contain duplicate vertices. ([#8808](https://github.com/mapbox/mapbox-gl-native/pull/8808))
+* Added struct boxing to `MGLCoordinateSpan`, `MGLCoordinateBounds`, `MGLOfflinePackProgress`, and `MGLTransition`. ([#9343](https://github.com/mapbox/mapbox-gl-native/pull/9343))
## 0.4.1 - April 8, 2017
diff --git a/platform/macos/DEVELOPING.md b/platform/macos/DEVELOPING.md
index fecde1fb38..562f1291c2 100644
--- a/platform/macos/DEVELOPING.md
+++ b/platform/macos/DEVELOPING.md
@@ -1,12 +1,12 @@
-# Contributing to the Mapbox macOS SDK
+# Contributing to the Mapbox Maps SDK for macOS
-This document explains how to build the Mapbox macOS SDK from source. It is intended for advanced developers who wish to contribute to Mapbox GL and the Mapbox iOS SDK.
+This document explains how to build the Mapbox Maps SDK for macOS from source. It is intended for advanced developers who wish to contribute to Mapbox GL and the Mapbox Maps SDK for iOS.
## Requirements
-The Mapbox macOS SDK and the macosapp demo application run on macOS 10.10.0 or above.
+The Mapbox Maps SDK for macOS and the macosapp demo application run on macOS 10.10.0 or above.
-The Mapbox macOS SDK requires Xcode 8.0 or above.
+The Mapbox Maps SDK for macOS requires Xcode 8.0 or above.
## Building the SDK
@@ -44,7 +44,7 @@ The products of these build commands can be found in the `build/macos/pkg` folde
### Making any symbol public
-To add any Objective-C type, constant, or member to the iOS SDK’s public interface:
+To add any Objective-C type, constant, or member to the macOS maps SDK’s public interface:
1. Ensure that the symbol is pure Objective-C and does not rely on any language features specific to Objective-C++ or the C11 dialect of C – so no namespaced types or classes named with emoji! 🙃 Most projects that depend on this SDK are either written in pure Objective-C (GNU99 dialect) or Swift, which cannot yet bridge C++ types.
1. Name the symbol according to [Cocoa naming conventions](https://developer.apple.com/library/prerelease/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html#//apple_ref/doc/uid/10000146i). Use the `MGL` class prefix to avoid conflicts with client code. If the symbol has an analogue in MapKit, name the symbol according to MapKit.
@@ -52,40 +52,40 @@ To add any Objective-C type, constant, or member to the iOS SDK’s public inter
### Making a type or constant public
-To add an Objective-C class, protocol, category, typedef, enumeration, or global constant to the macOS SDK’s public interface:
+To add an Objective-C class, protocol, category, typedef, enumeration, or global constant to the macOS maps SDK’s public interface:
1. _(Optional.)_ Add the macro `MGL_EXPORT` prior to the declaration for classes and global constants. To use this macro, include `MGLFoundation.h`. You can check whether all public symbols are exported correctly by running `make check-public-symbols`.
1. _(Optional.)_ Add the type or constant’s name to the relevant category in the `custom_categories` section of [the jazzy configuration file](./jazzy.yml). This is required for classes and protocols and also recommended for any other type that is strongly associated with a particular class or protocol. If you leave out this step, the symbol will appear in an “Other” section in the generated HTML documentation’s table of contents.
-1. _(Optional.)_ If the symbol would also be publicly exposed in the iOS SDK, consult [the companion iOS document](../ios/DEVELOPING.md#making-a-type-or-constant-public) for further instructions.
+1. _(Optional.)_ If the symbol would also be publicly exposed in the iOS maps SDK, consult [the companion iOS document](../ios/DEVELOPING.md#making-a-type-or-constant-public) for further instructions.
### Adding a source code file
-To add an Objective-C header or implementation file to the macOS SDK:
+To add an Objective-C header or implementation file to the macOS maps SDK:
1. Add the file to the “dynamic” target’s Headers or Compile Sources build phase, as appropriate. You can either use the Build Phases tab of the project editor or the Target Membership section of the File inspector.
1. Audit new headers for nullability. Typically, you will wrap a header with `NS_ASSUME_NONNULL_BEGIN` and `NS_ASSUME_NONNULL_END`.
1. _(Optional.)_ If it’s a public header, change its visibility from Project to Public and import it in [the macOS SDK’s umbrella header](./src/Mapbox.h).
-1. _(Optional.)_ If the file would also be used by the iOS SDK, make sure it’s in [platform/darwin/src/](../darwin/src/), then consult [the companion iOS document](../ios/DEVELOPING.md#adding-a-source-code-file) for further instructions.
+1. _(Optional.)_ If the file would also be used by the iOS maps SDK, make sure it’s in [platform/darwin/src/](../darwin/src/), then consult [the companion iOS document](../ios/DEVELOPING.md#adding-a-source-code-file) for further instructions.
### Adding a resource
-To add a resource (such as an image, SSL certificate, property list, or strings table) to the macOS SDK:
+To add a resource (such as an image, SSL certificate, property list, or strings table) to the macOS maps SDK:
1. Add the header to the Copy Bundle Resources build phase of the “dynamic” target. You can either use the Build Phases tab of the project editor or the Target Membership section of the File inspector.
-1. _(Optional.)_ If the resource would also be used by the iOS SDK, make sure it’s in [platform/darwin/resources/](../darwin/resources/), then consult [the companion iOS document](../ios/DEVELOPING.md#adding-a-resource) for further instructions.
+1. _(Optional.)_ If the resource would also be used by the iOS maps SDK, make sure it’s in [platform/darwin/resources/](../darwin/resources/), then consult [the companion iOS document](../ios/DEVELOPING.md#adding-a-resource) for further instructions.
### Adding user-facing text
-To add or update text that the user may see in the macOS SDK:
+To add or update text that the user may see in the macOS maps SDK:
1. Make sure the implementation file imports [NSBundle+MGLAdditions.h](../darwin/src/NSBundle+MGLAdditions.h).
1. Use the `NSLocalizedStringWithDefaultValue()` macro:
* `key` is a unique identifier that won’t change if the user-facing text ever needs to change.
- * `tbl` is `Foundation` in code shared between the iOS and macOS SDKs, or `nil` otherwise.
+ * `tbl` is `Foundation` in code shared between the iOS and macOS maps SDKs, or `nil` otherwise.
* `bundle` is `nil`; the redefined macro looks for the SDK bundle at runtime and ignores this argument.
* `val` is the English string.
1. _(Optional.)_ When dealing with a number followed by a pluralized word, do not split the string. Instead, use a format string and make `val` ambiguous, like `%d file(s)`. Then pluralize for English in the appropriate [.stringsdict file](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html). See [platform/darwin/resources/en.lproj/Foundation.stringsdict](../darwin/resources/en.lproj/Foundation.stringsdict) for an example. Localizers should do likewise for their languages.
-1. Run `make genstrings` and commit any changes it makes to .strings files. The make rule also updates the iOS SDK’s strings tables.
+1. Run `make genstrings` and commit any changes it makes to .strings files. The make rule also updates the iOS maps SDK’s strings tables.
### Adding a localization
@@ -141,6 +141,6 @@ The demo applications use Mapbox vector tiles, which require a Mapbox account an
## Using macosapp
-Through the macOS SDK, the demo application supports a variety of standard gestures and keyboard shortcuts. For more details, open Mapbox GL Help from the Help menu.
+Through the macOS maps SDK, the demo application supports a variety of standard gestures and keyboard shortcuts. For more details, open Mapbox GL Help from the Help menu.
-You can also [integrate the Mapbox macOS SDK into your own Cocoa application](INSTALL.md).
+You can also [integrate the Mapbox Maps SDK for macOS into your own Cocoa application](INSTALL.md).
diff --git a/platform/macos/INSTALL.md b/platform/macos/INSTALL.md
index cbb9d7d8a6..c723d3e062 100644
--- a/platform/macos/INSTALL.md
+++ b/platform/macos/INSTALL.md
@@ -1,10 +1,10 @@
-# Integrating a custom build of the Mapbox macOS SDK into your application
+# Integrating a custom build of the Mapbox Maps SDK for macOS into your application
-This document explains how to build a development version of the Mapbox macOS SDK from source and integrate it into your own Cocoa application. This process is for advanced developers who want to get a glimpse of the SDK’s development between releases. To use a production-ready version of the SDK, see the [Mapbox macOS SDK homepage](https://mapbox.github.io/mapbox-gl-native/macos/).
+This document explains how to build a development version of the Mapbox Maps SDK for macOS from source and integrate it into your own Cocoa application. This process is for advanced developers who want to get a glimpse of the SDK’s development between releases. To use a production-ready version of the SDK, see the [Mapbox Maps SDK for macOS homepage](https://mapbox.github.io/mapbox-gl-native/macos/).
### Requirements
-The Mapbox macOS SDK 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.
+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
@@ -71,4 +71,4 @@ script AppDelegate
end script
```
-For further instructions, consult the [macOS SDK documentation](https://mapbox.github.io/mapbox-gl-native/macos/) or run `make xdocument` to generate the documentation. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking.
+For further instructions, consult the [macOS SDK documentation](https://mapbox.github.io/mapbox-gl-native/macos/) or run `make xdocument` to generate the documentation. The [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox Maps SDK for macOS with few differences, mostly around unimplemented features like user location tracking.
diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec
index c621a7312b..dd60957bb5 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.4.1'
+ version = '0.5.1'
m.name = 'Mapbox-macOS-SDK-symbols'
m.version = "#{version}-symbols"
diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec
index 2417fb11ec..533d7499f5 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.4.1'
+ version = '0.5.1'
m.name = 'Mapbox-macOS-SDK'
m.version = version
diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib
index 9a8cf05c16..d014676caa 100644
--- a/platform/macos/app/Base.lproj/MainMenu.xib
+++ b/platform/macos/app/Base.lproj/MainMenu.xib
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -128,6 +128,12 @@
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
+ <menuItem title="Export Image…" id="vjX-0E-kLO">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="takeSnapshot:" target="-1" id="H06-sU-n4U"/>
+ </connections>
+ </menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<connections>
@@ -559,6 +565,12 @@
<connections>
<action selector="addAnimatedImageSource:" target="-1" id="TuN-Pa-hTG"/>
</connections>
+ </menuItem>
+ <menuItem title="Add Graticule" id="Msk-p2-Lwt">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="insertGraticuleLayer:" target="-1" id="LE5-lz-kx4"/>
+ </connections>
</menuItem>
<menuItem title="Show All Annnotations" keyEquivalent="A" id="yMj-uM-8SN">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
@@ -568,8 +580,8 @@
</menuItem>
<menuItem title="Remove All Annotations" id="6rC-68-vk0">
<string key="keyEquivalent" base64-UTF8="YES">
-CA
-</string>
+ CA
+ </string>
<connections>
<action selector="removeAllAnnotations:" target="-1" id="6v3-0E-LsR"/>
</connections>
@@ -654,7 +666,7 @@ CA
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="131" width="350" height="84"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="eA4-n3-qPe">
<rect key="frame" x="0.0" y="0.0" width="350" height="84"/>
<autoresizingMask key="autoresizingMask"/>
@@ -730,7 +742,7 @@ CA
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="830" y="430" width="400" height="300"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="8ha-hw-zOD">
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
<autoresizingMask key="autoresizingMask"/>
@@ -739,7 +751,7 @@ CA
<rect key="frame" x="-1" y="20" width="402" height="281"/>
<clipView key="contentView" id="J9U-Yx-o2S">
<rect key="frame" x="1" y="0.0" width="400" height="280"/>
- <autoresizingMask key="autoresizingMask"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<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"/>
diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib
index d95f21b2e9..0394f38533 100644
--- a/platform/macos/app/Base.lproj/MapDocument.xib
+++ b/platform/macos/app/Base.lproj/MapDocument.xib
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12120"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="12121"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -48,7 +48,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="1280" height="777"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="TuG-C5-zLS">
<rect key="frame" x="0.0" y="0.0" width="642" height="480"/>
<autoresizingMask key="autoresizingMask"/>
diff --git a/platform/macos/app/Info.plist b/platform/macos/app/Info.plist
index 21b86bfc75..8bb225358c 100644
--- a/platform/macos/app/Info.plist
+++ b/platform/macos/app/Info.plist
@@ -2,6 +2,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>MGLIdeographicFontFamilyName</key>
+ <string>PingFang TC</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDocumentTypes</key>
diff --git a/platform/macos/app/MGLVectorSource+MBXAdditions.h b/platform/macos/app/MGLVectorSource+MBXAdditions.h
deleted file mode 100644
index 1e25ee5a60..0000000000
--- a/platform/macos/app/MGLVectorSource+MBXAdditions.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#import <Mapbox/Mapbox.h>
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface MGLVectorSource (MBXAdditions)
-
-+ (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/macos/app/MGLVectorSource+MBXAdditions.m b/platform/macos/app/MGLVectorSource+MBXAdditions.m
deleted file mode 100644
index 323bc74366..0000000000
--- a/platform/macos/app/MGLVectorSource+MBXAdditions.m
+++ /dev/null
@@ -1,53 +0,0 @@
-#import "MGLVectorSource+MBXAdditions.h"
-
-@implementation MGLVectorSource (MBXAdditions)
-
-+ (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/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 94bf18dea1..eb20063996 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -3,9 +3,10 @@
#import "AppDelegate.h"
#import "LimeGreenStyleLayer.h"
#import "DroppedPinAnnotation.h"
+#import "MGLMapsnapshotter.h"
#import "MGLStyle+MBXAdditions.h"
-#import "MGLVectorSource+MBXAdditions.h"
+#import "MGLVectorSource_Private.h"
#import <Mapbox/Mapbox.h>
@@ -49,7 +50,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
return flattenedShapes;
}
-@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, NSSplitViewDelegate, MGLMapViewDelegate>
+@interface MapDocument () <NSWindowDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, NSSplitViewDelegate, MGLMapViewDelegate, MGLComputedShapeSourceDataSource>
@property (weak) IBOutlet NSArrayController *styleLayersArrayController;
@property (weak) IBOutlet NSTableView *styleLayersTableView;
@@ -73,6 +74,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
BOOL _isTouringWorld;
BOOL _isShowingPolygonAndPolylineAnnotations;
BOOL _isShowingAnimatedAnnotation;
+
+ // Snapshotter
+ MGLMapSnapshotter* snapshotter;
}
#pragma mark Lifecycle
@@ -153,6 +157,66 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
camera.heading, camera.pitch]];
}
+#pragma mark File methods
+
+- (IBAction)takeSnapshot:(id)sender {
+ MGLMapCamera *camera = self.mapView.camera;
+
+ MGLMapSnapshotOptions *options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:self.mapView.styleURL camera:camera size:self.mapView.bounds.size];
+ options.zoomLevel = self.mapView.zoomLevel;
+
+ // Create and start the snapshotter
+ snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options];
+ [snapshotter startWithCompletionHandler:^(MGLMapSnapshot *snapshot, NSError *error) {
+ if (error) {
+ NSLog(@"Could not load snapshot: %@", error.localizedDescription);
+ } else {
+ // Set the default name for the file and show the panel.
+ NSSavePanel *panel = [NSSavePanel savePanel];
+ panel.nameFieldStringValue = [self.mapView.styleURL.lastPathComponent.stringByDeletingPathExtension stringByAppendingPathExtension:@"png"];
+ panel.allowedFileTypes = [@[(NSString *)kUTTypePNG] arrayByAddingObjectsFromArray:[NSBitmapImageRep imageUnfilteredTypes]];
+
+ [panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
+ if (result == NSFileHandlingPanelOKButton) {
+ // Write the contents in the new format.
+ NSURL *fileURL = panel.URL;
+
+ NSBitmapImageRep *bitmapRep;
+ for (NSImageRep *imageRep in snapshot.image.representations) {
+ if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
+ bitmapRep = (NSBitmapImageRep *)imageRep;
+ break; // stop on first bitmap rep we find
+ }
+ }
+
+ if (!bitmapRep) {
+ bitmapRep = [NSBitmapImageRep imageRepWithData:snapshot.image.TIFFRepresentation];
+ }
+
+ CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileURL.pathExtension, NULL /* inConformingToUTI */);
+ NSBitmapImageFileType fileType = NSTIFFFileType;
+ if (UTTypeConformsTo(uti, kUTTypePNG)) {
+ fileType = NSPNGFileType;
+ } else if (UTTypeConformsTo(uti, kUTTypeGIF)) {
+ fileType = NSGIFFileType;
+ } else if (UTTypeConformsTo(uti, kUTTypeJPEG2000)) {
+ fileType = NSJPEG2000FileType;
+ } else if (UTTypeConformsTo(uti, kUTTypeJPEG)) {
+ fileType = NSJPEGFileType;
+ } else if (UTTypeConformsTo(uti, kUTTypeBMP)) {
+ fileType = NSBitmapImageFileTypeBMP;
+ }
+
+ NSData *imageData = [bitmapRep representationUsingType:fileType properties:@{}];
+ [imageData writeToURL:fileURL atomically:NO];
+ }
+ }];
+
+ }
+ snapshotter = nil;
+ }];
+}
+
#pragma mark View methods
- (IBAction)showStyle:(id)sender {
@@ -183,10 +247,10 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
styleURL = [MGLStyle satelliteStreetsStyleURL];
break;
case 7:
- styleURL = [MGLStyle trafficDayStyleURL];
+ styleURL = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"];
break;
case 8:
- styleURL = [MGLStyle trafficNightStyleURL];
+ styleURL = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"];
break;
default:
NSAssert(NO, @"Cannot set style from control with tag %li", (long)tag);
@@ -344,52 +408,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
}
- (void)updateLabels {
- MGLStyle *style = self.mapView.style;
- NSString *preferredLanguage = _isLocalizingLabels ? [MGLVectorSource preferredMapboxStreetsLanguage] : nil;
- NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary];
- for (MGLSymbolStyleLayer *layer in style.layers) {
- if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
- continue;
- }
-
- MGLVectorSource *source = (MGLVectorSource *)[style 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];
- layer.text = [MGLStyleValue<NSString *> valueWithRawValue:stringByLocalizingString(textField)];
- }
- else if ([layer.text isKindOfClass:[MGLCameraStyleFunction class]]) {
- MGLCameraStyleFunction *function = (MGLCameraStyleFunction<NSString *> *)layer.text;
- NSMutableDictionary *stops = function.stops.mutableCopy;
- [stops enumerateKeysAndObjectsUsingBlock:^(NSNumber *zoomLevel, MGLConstantStyleValue<NSString *> *stop, BOOL *done) {
- NSString *textField = stop.rawValue;
- stops[zoomLevel] = [MGLStyleValue<NSString *> valueWithRawValue:stringByLocalizingString(textField)];
- }];
- function.stops = stops;
- layer.text = function;
- }
- }
+ self.mapView.style.localizesLabels = _isLocalizingLabels;
}
- (void)applyPendingState {
@@ -679,6 +698,47 @@ 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"];
+ }
+
+ MGLComputedShapeSource *source = [[MGLComputedShapeSource alloc] initWithIdentifier:@"graticule"
+ options:@{MGLShapeSourceOptionMaximumZoomLevel:@14}];
+ 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 = [MGLStyleValue valueWithRawValue:@"{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];
+}
+
#pragma mark Offline packs
- (IBAction)addOfflinePack:(id)sender {
@@ -854,10 +914,10 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
state = [styleURL isEqual:[MGLStyle satelliteStreetsStyleURL]];
break;
case 7:
- state = [styleURL isEqual:[MGLStyle trafficDayStyleURL]];
+ state = [styleURL isEqual:[NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"]];
break;
case 8:
- state = [styleURL isEqual:[MGLStyle trafficNightStyleURL]];
+ state = [styleURL isEqual:[NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"]];
break;
default:
return NO;
@@ -992,6 +1052,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
if (menuItem.action == @selector(insertCustomStyleLayer:)) {
return ![self.mapView.style layerWithIdentifier:@"mbx-custom"];
}
+ if (menuItem.action == @selector(insertGraticuleLayer:)) {
+ return ![self.mapView.style sourceWithIdentifier:@"graticule"];
+ }
if (menuItem.action == @selector(showAllAnnotations:) || menuItem.action == @selector(removeAllAnnotations:)) {
return self.mapView.annotations.count > 0;
}
@@ -1008,6 +1071,9 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
if (menuItem.action == @selector(giveFeedback:)) {
return YES;
}
+ if (menuItem.action == @selector(takeSnapshot:)) {
+ return !(snapshotter && [snapshotter isLoading]);
+ }
return NO;
}
@@ -1163,6 +1229,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/bg.lproj/Localizable.strings b/platform/macos/app/bg.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/macos/app/bg.lproj/Localizable.strings
diff --git a/platform/macos/app/hu.lproj/Localizable.strings b/platform/macos/app/hu.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/macos/app/hu.lproj/Localizable.strings
diff --git a/platform/macos/bitrise.yml b/platform/macos/bitrise.yml
index 1f2495dab2..057193967e 100644
--- a/platform/macos/bitrise.yml
+++ b/platform/macos/bitrise.yml
@@ -12,39 +12,9 @@ workflows:
primary:
steps:
- script:
- title: Build
- inputs:
- - content: |-
- #!/bin/bash
- set -eu -o pipefail
- brew install cmake
- gem install xcpretty --no-rdoc --no-ri
- export BUILDTYPE=Debug
- export XCPRETTY="| tee ${BITRISE_DEPLOY_DIR}/raw-xcodebuild-output.txt | xcpretty --color --report html --output ${BITRISE_DEPLOY_DIR}/xcode-test-results.html"
- make run-test
- - deploy-to-bitrise-io:
- title: Deploy to Bitrise.io
- inputs:
- - deploy_path: "test/fixtures"
- - notify_user_groups: none
- - is_compress: 'true'
- - slack:
- title: Post to Slack
+ title: Skip Workflow
inputs:
- - webhook_url: "$SLACK_HOOK_URL"
- - channel: "#gl-bots"
- - from_username: 'Bitrise macOS'
- - from_username_on_error: 'Bitrise macOS'
- - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- passed'
- - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- failed'
- - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
+ - content: echo "This workflow is obsolete — see CircleCi."
nightly-release:
steps:
- script:
diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake
index 8dc3c38245..e929bb55c6 100644
--- a/platform/macos/config.cmake
+++ b/platform/macos/config.cmake
@@ -1,42 +1,26 @@
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10)
-mason_use(glfw VERSION 2017-02-09-77a8f10)
-mason_use(boost_libprogram_options VERSION 1.62.0)
+mason_use(glfw VERSION 2017-07-13-67c9155)
mason_use(gtest VERSION 1.8.0)
-mason_use(benchmark VERSION 1.0.0-1)
+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)
target_sources(mbgl-core
- # File source
- PRIVATE platform/darwin/src/http_file_source.mm
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
-
- # Default styles
- PRIVATE platform/default/mbgl/util/default_styles.hpp
- PRIVATE platform/default/mbgl/util/default_styles.cpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
- PRIVATE platform/default/sqlite3.hpp
-
# Misc
PRIVATE platform/darwin/mbgl/storage/reachability.h
PRIVATE platform/darwin/mbgl/storage/reachability.m
+ PRIVATE platform/darwin/src/CFHandle.hpp
+ PRIVATE platform/darwin/src/local_glyph_rasterizer.mm
PRIVATE platform/darwin/src/logging_nslog.mm
PRIVATE platform/darwin/src/nsthread.mm
PRIVATE platform/darwin/src/string_nsstring.mm
PRIVATE platform/default/bidi.cpp
+ PRIVATE platform/default/thread_local.cpp
PRIVATE platform/default/utf.cpp
# Image handling
@@ -45,13 +29,15 @@ macro(mbgl_platform_core)
PRIVATE platform/default/png_writer.cpp
# Headless view
+ PRIVATE platform/default/mbgl/gl/headless_frontend.cpp
+ 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/darwin/src/headless_backend_cgl.cpp
- PRIVATE platform/default/mbgl/gl/headless_display.hpp
- PRIVATE platform/darwin/src/headless_display_cgl.cpp
- PRIVATE platform/default/mbgl/gl/offscreen_view.cpp
- PRIVATE platform/default/mbgl/gl/offscreen_view.hpp
+
+ # Snapshotting
+ PRIVATE platform/default/mbgl/map/map_snapshotter.cpp
+ PRIVATE platform/default/mbgl/map/map_snapshotter.hpp
# Thread pool
PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp
@@ -61,11 +47,11 @@ macro(mbgl_platform_core)
)
target_add_mason_package(mbgl-core PUBLIC geojson)
+ target_add_mason_package(mbgl-core PUBLIC polylabel)
target_add_mason_package(mbgl-core PRIVATE icu)
target_compile_options(mbgl-core
PRIVATE -fobjc-arc
- PRIVATE -fvisibility=hidden
)
target_include_directories(mbgl-core
@@ -76,18 +62,39 @@ macro(mbgl_platform_core)
target_link_libraries(mbgl-core
PUBLIC "-lz"
PUBLIC "-framework Foundation"
+ PUBLIC "-framework CoreText"
PUBLIC "-framework CoreGraphics"
PUBLIC "-framework OpenGL"
PUBLIC "-framework ImageIO"
PUBLIC "-framework CoreServices"
PUBLIC "-framework SystemConfiguration"
+ )
+endmacro()
+
+
+macro(mbgl_filesource)
+ target_sources(mbgl-filesource
+ # File source
+ PRIVATE platform/darwin/src/http_file_source.mm
+
+ # Database
+ PRIVATE platform/default/sqlite3.cpp
+ )
+
+ target_compile_options(mbgl-filesource
+ PRIVATE -fobjc-arc
+ )
+
+ target_link_libraries(mbgl-filesource
PUBLIC "-lsqlite3"
+ PUBLIC "-framework Foundation"
)
endmacro()
macro(mbgl_platform_glfw)
target_link_libraries(mbgl-glfw
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
endmacro()
@@ -95,6 +102,7 @@ endmacro()
macro(mbgl_platform_render)
target_link_libraries(mbgl-render
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
endmacro()
@@ -102,6 +110,7 @@ endmacro()
macro(mbgl_platform_offline)
target_link_libraries(mbgl-offline
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
endmacro()
@@ -112,26 +121,23 @@ 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
)
endmacro()
macro(mbgl_platform_benchmark)
- target_compile_options(mbgl-benchmark
- PRIVATE -fvisibility=hidden
- )
-
target_sources(mbgl-benchmark
PRIVATE benchmark/src/main.cpp
)
@@ -143,15 +149,12 @@ macro(mbgl_platform_benchmark)
)
target_link_libraries(mbgl-benchmark
+ PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
endmacro()
macro(mbgl_platform_node)
- target_compile_options(mbgl-node
- PRIVATE -fvisibility=hidden
- )
-
target_link_libraries(mbgl-node
PRIVATE "-Wl,-bind_at_load"
)
diff --git a/platform/macos/docs/guides/Info.plist Keys.md b/platform/macos/docs/guides/Info.plist Keys.md
index f61ad8c7a7..be6096d167 100644
--- a/platform/macos/docs/guides/Info.plist Keys.md
+++ b/platform/macos/docs/guides/Info.plist Keys.md
@@ -1,6 +1,6 @@
# Info.plist Keys
-The Mapbox macOS SDK supports custom `Info.plist` keys in your application in order to configure various settings.
+The Mapbox Maps SDK for macOS supports custom `Info.plist` keys in your application in order to configure various settings.
## MGLMapboxAccessToken
@@ -15,3 +15,7 @@ As an alternative, you can use `+[MGLAccountManager setAccessToken:]` to set a t
Use this key if you need to customize the API base URL used throughout the SDK. If unset, the default Mapbox API is used.
The default value is `https://api.mapbox.com`.
+
+## MGLIdeographicFontFamilyName
+
+The name of the font family to use for client-side text rendering of CJK ideographs. Set this to the name of a font family which will be available at run time, e.g. `PingFang TC` (iOS 9+), `Heiti TC` (iOS 8+), another appropriate built-in font, or a font provided by your application. Note that if a non-existent font is specified, iOS will fall back to using Helvetica which is likely not to include support for the glyphs needed to render maps in your application. \ No newline at end of file
diff --git a/platform/macos/docs/img/screenshot.jpg b/platform/macos/docs/img/screenshot.jpg
index 5341a3d6f0..a8c64a69fe 100644
--- a/platform/macos/docs/img/screenshot.jpg
+++ b/platform/macos/docs/img/screenshot.jpg
Binary files differ
diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml
index fcd4bfd301..3267ccb954 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' />
@@ -61,7 +61,9 @@ custom_categories:
children:
- MGLSource
- MGLTileSource
+ - MGLAbstractShapeSource
- MGLShapeSource
+ - MGLComputedShapeSource
- MGLRasterSource
- MGLVectorSource
- name: Style Layers
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index 723bbc6f4b..ded17d003a 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 */; };
+ 07D9474B1F6743F000E37934 /* MGLAbstractShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07D947491F6741F500E37934 /* MGLAbstractShapeSource.mm */; };
+ 07D9474C1F67441500E37934 /* MGLAbstractShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D947481F6741F500E37934 /* MGLAbstractShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 07D9474D1F67441B00E37934 /* MGLAbstractShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D947471F6741F500E37934 /* MGLAbstractShapeSource_Private.h */; };
+ 07F8E2F71F674C8800F794BB /* MGLComputedShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 07F8E2F81F674C9000F794BB /* MGLComputedShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */; };
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, ); }; };
@@ -56,16 +62,14 @@
35C6DF871E214C1800ACA483 /* MGLDistanceFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35C6DF861E214C1800ACA483 /* MGLDistanceFormatterTests.m */; };
35D65C5A1D65AD5500722C23 /* NSDate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D65C581D65AD5500722C23 /* NSDate+MGLAdditions.h */; };
35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */; };
+ 3EA9317388DC9A0BF46B7674 /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */; };
+ 3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */; };
4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4031ACFB1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift */; };
4031AD031E9FD6AA00A3EA26 /* MGLSDKTestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4031AD011E9FD6A300A3EA26 /* MGLSDKTestHelpers.swift */; };
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 */; };
@@ -78,8 +82,15 @@
556660D61E1D07E400E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D51E1D07E400E2C41B /* MGLVersionNumber.m */; };
558DE7A61E56161C00C7916D /* MGLFoundation_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */; };
558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 558DE7A51E56161C00C7916D /* MGLFoundation.mm */; };
+ 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 */; };
+ 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 */; };
@@ -236,7 +247,6 @@
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 */; };
- DAF0D81C1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */; };
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 */; };
@@ -276,7 +286,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 /* MGLAbstractShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource_Private.h; sourceTree = "<group>"; };
+ 07D947481F6741F500E37934 /* MGLAbstractShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAbstractShapeSource.h; sourceTree = "<group>"; };
+ 07D947491F6741F500E37934 /* MGLAbstractShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAbstractShapeSource.mm; sourceTree = "<group>"; };
+ 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource.h; sourceTree = "<group>"; };
+ 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLComputedShapeSource.mm; sourceTree = "<group>"; };
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>"; };
@@ -324,6 +340,8 @@
35C6DF861E214C1800ACA483 /* MGLDistanceFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLDistanceFormatterTests.m; path = ../../darwin/test/MGLDistanceFormatterTests.m; sourceTree = "<group>"; };
35D65C581D65AD5500722C23 /* NSDate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+MGLAdditions.h"; sourceTree = "<group>"; };
35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDate+MGLAdditions.mm"; sourceTree = "<group>"; };
+ 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererConfiguration.h; sourceTree = "<group>"; };
+ 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRendererConfiguration.mm; sourceTree = "<group>"; };
4031ACFB1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLMapViewDelegateIntegrationTests.swift; sourceTree = "<group>"; };
4031AD011E9FD6A300A3EA26 /* MGLSDKTestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MGLSDKTestHelpers.swift; path = ../../darwin/test/MGLSDKTestHelpers.swift; sourceTree = "<group>"; };
4049C2A11DB6CE7800B3F799 /* MGLPointCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLPointCollection.h; sourceTree = "<group>"; };
@@ -331,12 +349,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>"; };
@@ -347,10 +361,16 @@
556660D51E1D07E400E2C41B /* MGLVersionNumber.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MGLVersionNumber.m; path = ../../darwin/test/MGLVersionNumber.m; sourceTree = "<group>"; };
558DE7A41E56161C00C7916D /* MGLFoundation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFoundation_Private.h; sourceTree = "<group>"; };
558DE7A51E56161C00C7916D /* MGLFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFoundation.mm; sourceTree = "<group>"; };
+ 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
+ 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>"; };
@@ -373,6 +393,12 @@
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>"; };
+ DA3389611FA3EDCE001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA3389621FA3EDEF001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA3389631FA3EDF5001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
+ DA3389641FA3EE00001EA329 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA33896C1FA3EF51001EA329 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA35A2A31CC9EB1A00E826B2 /* MGLCoordinateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCoordinateFormatter.h; sourceTree = "<group>"; };
DA35A2A51CC9EB2700E826B2 /* MGLCoordinateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCoordinateFormatter.m; sourceTree = "<group>"; };
DA35A2A71CC9F41600E826B2 /* MGLCoordinateFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLCoordinateFormatterTests.m; path = ../../darwin/test/MGLCoordinateFormatterTests.m; sourceTree = "<group>"; };
@@ -393,21 +419,25 @@
DA6023EF1E4CE8E500DBFF23 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Foundation.strings; sourceTree = "<group>"; };
DA6023F01E4CE8FF00DBFF23 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B131E68850300CB7F44 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
- DA618B141E68852C00CB7F44 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA618B141E68852C00CB7F44 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B151E6886DF00CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ca; path = ca.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B161E6886E000CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B171E68876C00CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Foundation.strings; sourceTree = "<group>"; };
- DA618B181E6887C600CB7F44 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA618B181E6887C600CB7F44 /* ca */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B231E6891ED00CB7F44 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Foundation.strings; sourceTree = "<group>"; };
DA618B241E6891F300CB7F44 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA618B271E68926E00CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
DA618B2A1E6892B500CB7F44 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
DA6408D51DA4E5DA00908C90 /* MGLVectorStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorStyleLayer.h; sourceTree = "<group>"; };
DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLVectorStyleLayer.m; sourceTree = "<group>"; };
+ DA704CBA1F6372E8004B3F28 /* ru */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DA704CBE1F637531004B3F28 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA704CBF1F637548004B3F28 /* hu */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA704CC61F666385004B3F28 /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Foundation.strings; sourceTree = "<group>"; };
DA7262051DEEDD460043BB89 /* MGLOpenGLStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLOpenGLStyleLayer.h; sourceTree = "<group>"; };
DA7262061DEEDD460043BB89 /* MGLOpenGLStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLOpenGLStyleLayer.mm; sourceTree = "<group>"; };
DA737ADE1E5914AD00AD2CDE /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
- DA737ADF1E5914D300AD2CDE /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DA737ADF1E5914D300AD2CDE /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
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>"; };
@@ -467,7 +497,7 @@
DAA32CB31E4C4CC3006F8D24 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CBA1E4C4F10006F8D24 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA32CC01E4C4F89006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Foundation.strings; sourceTree = "<group>"; };
- DAA32CC11E4C4F93006F8D24 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DAA32CC11E4C4F93006F8D24 /* vi */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleLayerIconTransformer.h; sourceTree = "<group>"; };
DAA48EFC1D6A4731006A7E36 /* StyleLayerIconTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StyleLayerIconTransformer.m; sourceTree = "<group>"; };
DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillExtrusionStyleLayer.h; sourceTree = "<group>"; };
@@ -481,6 +511,8 @@
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>"; };
+ DACCD9C71F1F443B00BB09A1 /* fr */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Foundation.strings; sourceTree = "<group>"; };
+ 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>"; };
DAE6C2E11CC304F900DB3429 /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
@@ -573,9 +605,7 @@
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>"; };
- DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLVectorSource+MBXAdditions.h"; sourceTree = "<group>"; };
- DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLVectorSource+MBXAdditions.m"; sourceTree = "<group>"; };
- DAFBD0D51E3FA969000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; 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>"; };
DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = "<group>"; };
@@ -596,6 +626,7 @@
buildActionMask = 2147483647;
files = (
5548BE781D09E718005DDE81 /* libmbgl-core.a in Frameworks */,
+ 55D120A31F7906E6004B6D81 /* libmbgl-filesource.a in Frameworks */,
52B5D17F1E5E26DF00BBCB48 /* libmbgl-loop-darwin.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -605,6 +636,7 @@
buildActionMask = 2147483647;
files = (
DAE0DD7A1D5F015A005A6BB1 /* libmbgl-core.a in Frameworks */,
+ 55D120A51F790A0C004B6D81 /* libmbgl-filesource.a in Frameworks */,
DAE6C3321CC30DB200DB3429 /* Mapbox.framework in Frameworks */,
52B5D1801E5E26DF00BBCB48 /* libmbgl-loop-darwin.a in Frameworks */,
);
@@ -655,6 +687,11 @@
3527427E1D4C242B00A1ECE6 /* Sources */ = {
isa = PBXGroup;
children = (
+ 07D947481F6741F500E37934 /* MGLAbstractShapeSource.h */,
+ 07D947471F6741F500E37934 /* MGLAbstractShapeSource_Private.h */,
+ 07D947491F6741F500E37934 /* MGLAbstractShapeSource.mm */,
+ 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */,
+ 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */,
352742831D4C244700A1ECE6 /* MGLRasterSource.h */,
DA7DC9821DED647F0027472F /* MGLRasterSource_Private.h */,
352742841D4C244700A1ECE6 /* MGLRasterSource.mm */,
@@ -709,6 +746,14 @@
name = "Test Helpers";
sourceTree = "<group>";
};
+ DA33895E1FA3E997001EA329 /* Recovered References */ = {
+ isa = PBXGroup;
+ children = (
+ 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */,
+ );
+ name = "Recovered References";
+ sourceTree = "<group>";
+ };
DA839E891CC2E3400062CAFB = {
isa = PBXGroup;
children = (
@@ -718,6 +763,7 @@
DAE6C31E1CC308BC00DB3429 /* Frameworks */,
DAE6C3C41CC31F7800DB3429 /* Configuration */,
DA839E931CC2E3400062CAFB /* Products */,
+ DA33895E1FA3E997001EA329 /* Recovered References */,
);
sourceTree = "<group>";
};
@@ -747,8 +793,6 @@
DA839E9E1CC2E3400062CAFB /* MapDocument.xib */,
DACB0C371E18DFFD005DDBEA /* MGLStyle+MBXAdditions.h */,
DACB0C381E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m */,
- DAF0D81A1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.h */,
- DAF0D81B1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m */,
DAE6C2E91CC3050F00DB3429 /* OfflinePackNameValueTransformer.h */,
DAE6C2EA1CC3050F00DB3429 /* OfflinePackNameValueTransformer.m */,
DAA48EFB1D6A4731006A7E36 /* StyleLayerIconTransformer.h */,
@@ -779,6 +823,7 @@
DA87A99A1DC9D88800810D09 /* Sources */ = {
isa = PBXGroup;
children = (
+ 07A240921F67566F002C8210 /* MGLComputedShapeSourceTests.m */,
DA87A9961DC9D88400810D09 /* MGLShapeSourceTests.mm */,
DA87A9971DC9D88400810D09 /* MGLTileSetTests.mm */,
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */,
@@ -896,8 +941,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 */,
@@ -928,10 +975,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 */,
@@ -966,6 +1009,7 @@
DAE6C31E1CC308BC00DB3429 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 55D120A41F7906E6004B6D81 /* libmbgl-filesource.a */,
5548BE7B1D0ACBBD005DDE81 /* libmbgl-loop-darwin.a */,
55D9B4B01D005D3900C1CCE2 /* libz.tbd */,
52BECB091CC5A26F009CD791 /* SystemConfiguration.framework */,
@@ -1044,8 +1088,13 @@
558DE7A51E56161C00C7916D /* MGLFoundation.mm */,
DAE6C34D1CC31E0400DB3429 /* MGLMapCamera.h */,
DAE6C36E1CC31E2A00DB3429 /* MGLMapCamera.mm */,
+ 92092EEE1F5EB10E00AF5130 /* MGLMapSnapshotter.h */,
+ 92092EEF1F5EB10E00AF5130 /* MGLMapSnapshotter.mm */,
DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */,
DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */,
+ 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */,
+ 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */,
+ 92F2C3EA1F0E3A1900268EC0 /* MGLRendererFrontend.h */,
DAE6C3571CC31E0400DB3429 /* MGLStyle.h */,
3537CA731D3F93A600380318 /* MGLStyle_Private.h */,
DAE6C37A1CC31E2A00DB3429 /* MGLStyle.mm */,
@@ -1097,15 +1146,16 @@
buildActionMask = 2147483647;
files = (
556660C61E1BEA0100E2C41B /* MGLFoundation.h in Headers */,
+ 07F8E2F71F674C8800F794BB /* MGLComputedShapeSource.h in Headers */,
DA8F258F1D51CA600010E6B5 /* MGLRasterStyleLayer.h in Headers */,
3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */,
DAE6C38D1CC31E2A00DB3429 /* MGLOfflineRegion_Private.h in Headers */,
DA7DC9831DED647F0027472F /* MGLRasterSource_Private.h in Headers */,
1753ED401E53CE6100A9FD90 /* MGLConversion.h in Headers */,
- 408AA8651DAEEE3400022900 /* MGLPolygon+MGLAdditions.h in Headers */,
DA8F259C1D51CB000010E6B5 /* MGLStyleValue_Private.h in Headers */,
DAE6C35B1CC31E0400DB3429 /* MGLAnnotation.h in Headers */,
DAE6C3B61CC31EF300DB3429 /* MGLMapView_Private.h in Headers */,
+ 92092EF01F5EB10E00AF5130 /* MGLMapSnapshotter.h in Headers */,
3527428D1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h in Headers */,
DA00FC8A1D5EEAC3009AABC8 /* MGLAttributionInfo.h in Headers */,
DAE6C3B21CC31EF300DB3429 /* MGLAttributionButton.h in Headers */,
@@ -1133,6 +1183,7 @@
DAE6C3861CC31E2A00DB3429 /* MGLGeometry_Private.h in Headers */,
DAE6C3841CC31E2A00DB3429 /* MGLAccountManager_Private.h in Headers */,
DAE6C3691CC31E0400DB3429 /* MGLTypes.h in Headers */,
+ 07D9474D1F67441B00E37934 /* MGLAbstractShapeSource_Private.h in Headers */,
DAE6C3991CC31E2A00DB3429 /* NSException+MGLAdditions.h in Headers */,
DA8F25871D51C9E10010E6B5 /* MGLBackgroundStyleLayer.h in Headers */,
4049C2A51DB6CE7F00B3F799 /* MGLPointCollection.h in Headers */,
@@ -1147,17 +1198,18 @@
35602BFA1D3EA99F0050646F /* MGLFillStyleLayer.h in Headers */,
DA35A2A41CC9EB1A00E826B2 /* MGLCoordinateFormatter.h in Headers */,
35C5D8491D6DD66D00E95907 /* NSCompoundPredicate+MGLAdditions.h in Headers */,
+ 07D9474C1F67441500E37934 /* MGLAbstractShapeSource.h in Headers */,
DD0902B31DB1AC6400C5BDCE /* MGLNetworkConfiguration.h in Headers */,
DAE6C3621CC31E0400DB3429 /* MGLOverlay.h in Headers */,
DAE6C3651CC31E0400DB3429 /* MGLPolyline.h in Headers */,
DAE6C39A1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h in Headers */,
+ 92F2C3EB1F0E3A1900268EC0 /* MGLRendererFrontend.h in Headers */,
DA8F258B1D51CA540010E6B5 /* MGLLineStyleLayer.h in Headers */,
35C6DF841E214C0400ACA483 /* MGLDistanceFormatter.h in Headers */,
DA8F25B21D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h in Headers */,
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 */,
DAE6C3601CC31E0400DB3429 /* MGLOfflineRegion.h in Headers */,
DAE6C3681CC31E0400DB3429 /* MGLTilePyramidOfflineRegion.h in Headers */,
@@ -1185,13 +1237,16 @@
DAE6C3A51CC31E9400DB3429 /* MGLMapView+IBAdditions.h in Headers */,
DA35A2AD1CCA091800E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
352742851D4C244700A1ECE6 /* MGLRasterSource.h in Headers */,
+ 9654C12D1FFC394700DB6A19 /* MGLPolygon_Private.h in Headers */,
408AA85B1DAEECFE00022900 /* MGLShape_Private.h in Headers */,
DACC22181CF3D4F700D220D9 /* MGLFeature_Private.h in Headers */,
+ 9654C12B1FFC38E000DB6A19 /* MGLPolyline_Private.h in Headers */,
DA6408D71DA4E5DA00908C90 /* MGLVectorStyleLayer.h in Headers */,
352742891D4C245800A1ECE6 /* MGLShapeSource.h in Headers */,
1F7454A41ECFB00300021D39 /* MGLLight.h in Headers */,
408AA8671DAEEE3900022900 /* NSDictionary+MGLAdditions.h in Headers */,
DAE6C3671CC31E0400DB3429 /* MGLStyle.h in Headers */,
+ 3EA9317388DC9A0BF46B7674 /* MGLRendererConfiguration.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1277,7 +1332,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = MBX;
- LastUpgradeCheck = 0800;
+ LastUpgradeCheck = 0920;
ORGANIZATIONNAME = Mapbox;
TargetAttributes = {
DA839E911CC2E3400062CAFB = {
@@ -1319,6 +1374,8 @@
ca,
fi,
nl,
+ hu,
+ bg,
);
mainGroup = DA839E891CC2E3400062CAFB;
productRefGroup = DA839E931CC2E3400062CAFB /* Products */;
@@ -1387,7 +1444,6 @@
DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */,
DA839E9A1CC2E3400062CAFB /* main.m in Sources */,
DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */,
- DAF0D81C1DFF567C00B28378 /* MGLVectorSource+MBXAdditions.m in Sources */,
DAE6C2F01CC3050F00DB3429 /* OfflinePackNameValueTransformer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1397,6 +1453,7 @@
buildActionMask = 2147483647;
files = (
07A019EF1ED665CD00ACD43E /* MGLImageSource.mm in Sources */,
+ 92092EF11F5EB10E00AF5130 /* MGLMapSnapshotter.mm in Sources */,
40ABDB561DB0022100372083 /* NSImage+MGLAdditions.mm in Sources */,
DAE6C3901CC31E2A00DB3429 /* MGLPointAnnotation.mm in Sources */,
DAE6C3981CC31E2A00DB3429 /* NSBundle+MGLAdditions.m in Sources */,
@@ -1418,6 +1475,7 @@
558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */,
DAE6C39D1CC31E2A00DB3429 /* NSString+MGLAdditions.m in Sources */,
3598195A1E02F611008FC139 /* NSCoder+MGLAdditions.mm in Sources */,
+ 07D9474B1F6743F000E37934 /* MGLAbstractShapeSource.mm in Sources */,
DAE6C3941CC31E2A00DB3429 /* MGLStyle.mm in Sources */,
DAE6C3871CC31E2A00DB3429 /* MGLGeometry.mm in Sources */,
3527428E1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm in Sources */,
@@ -1429,6 +1487,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 */,
@@ -1445,7 +1504,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 */,
@@ -1457,11 +1515,11 @@
35C5D8481D6DD66D00E95907 /* NSComparisonPredicate+MGLAdditions.mm in Sources */,
DA35A2AE1CCA091800E826B2 /* MGLCompassDirectionFormatter.m 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 */,
352742A11D4C25BD00A1ECE6 /* MGLStyleValue.mm in Sources */,
+ 3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1473,6 +1531,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 */,
@@ -1545,6 +1604,8 @@
DA618B161E6886E000CB7F44 /* ca */,
DA618B271E68926E00CB7F44 /* fi */,
DAE8CCAB1E6E8B72009B5CB0 /* nl */,
+ DA704CBE1F637531004B3F28 /* hu */,
+ DA3389611FA3EDCE001EA329 /* bg */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -1593,6 +1654,8 @@
DA618B181E6887C600CB7F44 /* ca */,
DA618B2A1E6892B500CB7F44 /* fi */,
DAE8CCAC1E6E8B8D009B5CB0 /* nl */,
+ DA704CBF1F637548004B3F28 /* hu */,
+ DA3389641FA3EE00001EA329 /* bg */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -1610,6 +1673,11 @@
DA618B171E68876C00CB7F44 /* ca */,
DA618B231E6891ED00CB7F44 /* lt */,
DAE9E0F21EB7BF39001E8E8B /* es */,
+ DACCD9C71F1F443B00BB09A1 /* fr */,
+ DA704CBA1F6372E8004B3F28 /* ru */,
+ DA704CC61F666385004B3F28 /* uk */,
+ DA3389601FA3EAC4001EA329 /* pt-BR */,
+ DA3389621FA3EDEF001EA329 /* bg */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -1628,6 +1696,9 @@
DAE8CCAA1E6E8605009B5CB0 /* ru */,
DA618B151E6886DF00CB7F44 /* ca */,
DA618B241E6891F300CB7F44 /* lt */,
+ DACFE7971F66EA0C00630DA8 /* vi */,
+ DA3389631FA3EDF5001EA329 /* bg */,
+ DA33896C1FA3EF51001EA329 /* hu */,
);
name = Foundation.stringsdict;
sourceTree = "<group>";
@@ -1645,7 +1716,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;
@@ -1653,12 +1726,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;
@@ -1693,7 +1773,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;
@@ -1701,12 +1783,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;
@@ -1720,6 +1809,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = NO;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SYMROOT = "$(PROJECT_DIR)/cmake";
};
name = Release;
@@ -1793,7 +1883,7 @@
FRAMEWORK_VERSION = A;
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
);
INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@@ -1805,7 +1895,7 @@
OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_LDFLAGS = (
"$(mbgl_core_LINK_LIBRARIES)",
- "$(mbgl_loop_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = Mapbox;
@@ -1830,7 +1920,7 @@
FRAMEWORK_VERSION = A;
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
);
INFOPLIST_FILE = "$(SRCROOT)/sdk/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@@ -1838,7 +1928,7 @@
OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_LDFLAGS = (
"$(mbgl_core_LINK_LIBRARIES)",
- "$(mbgl_loop_LINK_LIBRARIES)",
+ "$(mbgl_filesource_LINK_LIBRARIES)",
);
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = Mapbox;
@@ -1857,7 +1947,7 @@
COMBINE_HIDPI_IMAGES = YES;
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
);
INFOPLIST_FILE = test/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
@@ -1889,7 +1979,7 @@
COMBINE_HIDPI_IMAGES = YES;
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
- "$(polylabel_INCLUDE_DIRECTORIES)",
+ "$(mbgl_filesource_INCLUDE_DIRECTORIES)",
);
INFOPLIST_FILE = test/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
index a049928fdd..335441cc23 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0810"
+ LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -56,7 +56,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "8E8A234D3E364CDFA2918983"
+ BlueprintIdentifier = "E933DEF922EF4BC1AD735333"
BuildableName = "mbgl-test"
BlueprintName = "mbgl-test"
ReferencedContainer = "container:../../build/macos/mbgl.xcodeproj">
@@ -70,7 +70,7 @@
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
- BlueprintIdentifier = "11FCEF1E29744645907C1E4B"
+ BlueprintIdentifier = "1D3EE78DAFFA4A42AFD7160D"
BuildableName = "mbgl-benchmark"
BlueprintName = "mbgl-benchmark"
ReferencedContainer = "container:../../build/macos/mbgl.xcodeproj">
@@ -82,6 +82,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -111,6 +112,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
index 95351f7f04..4dadaee743 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/dynamic.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0810"
+ LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -40,6 +40,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -69,6 +70,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
index a58ef5c9cf..b0ce01fbf0 100644
--- a/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
+++ b/platform/macos/macos.xcodeproj/xcshareddata/xcschemes/macosapp.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "0810"
+ LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
@@ -55,6 +56,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
diff --git a/platform/macos/scripts/create_scheme.sh b/platform/macos/scripts/create_scheme.sh
deleted file mode 100755
index 5a609130d8..0000000000
--- a/platform/macos/scripts/create_scheme.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env bash
-
-set -u
-
-XCODEPROJ=${XCODEPROJ:-build/macos/mbgl.xcodeproj}
-OUTPUT="${XCODEPROJ}/xcshareddata/xcschemes/${SCHEME_NAME}.xcscheme"
-
-# Required ENV vars:
-# - SCHEME_TYPE: type of the scheme
-# - SCHEME_NAME: name of the scheme
-
-# Optional ENV vars:
-# - NODE_ARGUMENT (defaults to "")
-# - BUILDABLE_NAME (defaults ot SCHEME_NAME)
-# - BLUEPRINT_NAME (defaults ot SCHEME_NAME)
-
-
-# Try to reuse the existing Blueprint ID if the scheme already exists.
-if [ -f "${OUTPUT}" ]; then
- BLUEPRINT_ID=$(sed -n "s/[ \t]*BlueprintIdentifier *= *\"\([A-Z0-9]\{24\}\)\"/\\1/p" "${OUTPUT}" | head -1)
-fi
-
-NODE_ARGUMENT=${NODE_ARGUMENT:-}
-MAPBOX_ACCESS_TOKEN=${MAPBOX_ACCESS_TOKEN:-}
-BLUEPRINT_ID=${BLUEPRINT_ID:-$(hexdump -n 12 -v -e '/1 "%02X"' /dev/urandom)}
-BUILDABLE_NAME=${BUILDABLE_NAME:-${SCHEME_NAME}}
-BLUEPRINT_NAME=${BLUEPRINT_NAME:-${SCHEME_NAME}}
-
-mkdir -p "${XCODEPROJ}/xcshareddata/xcschemes"
-
-sed "\
-s#{{BLUEPRINT_ID}}#${BLUEPRINT_ID}#;\
-s#{{BLUEPRINT_NAME}}#${BLUEPRINT_NAME}#;\
-s#{{BUILDABLE_NAME}}#${BUILDABLE_NAME}#;\
-s#{{CONTAINER}}#${XCODEPROJ}#;\
-s#{{MAPBOX_ACCESS_TOKEN}}#${MAPBOX_ACCESS_TOKEN}#;\
-s#{{WORKING_DIRECTORY}}#$(pwd)#;\
-s#{{NODE_PATH}}#$(dirname `which node`)#;\
-s#{{NODE_ARGUMENT}}#${NODE_ARGUMENT}#" \
- platform/macos/scripts/${SCHEME_TYPE}.xcscheme > "${OUTPUT}"
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/bg.lproj/Localizable.strings b/platform/macos/sdk/bg.lproj/Localizable.strings
new file mode 100644
index 0000000000..5c71390076
--- /dev/null
+++ b/platform/macos/sdk/bg.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/ca.lproj/Localizable.strings b/platform/macos/sdk/ca.lproj/Localizable.strings
index b679fa0459..cd3073a8cb 100644
--- a/platform/macos/sdk/ca.lproj/Localizable.strings
+++ b/platform/macos/sdk/ca.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "El mapa no s’ha carregat a causa d’un error desconegut.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "El mapa no s’ha carregat perquè l’estil no es pot carregar.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "El mapa no s’ha carregat perquè s’ha corromput l’estil.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "El mapa no s’ha carregat perquè no es troba l’estil o bé és incompatible.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/default_marker.pdf b/platform/macos/sdk/default_marker.pdf
index 4e2e332301..d3e0e2ce12 100644
--- a/platform/macos/sdk/default_marker.pdf
+++ b/platform/macos/sdk/default_marker.pdf
Binary files differ
diff --git a/platform/macos/sdk/es.lproj/Localizable.strings b/platform/macos/sdk/es.lproj/Localizable.strings
index 4ebb97b440..8a9b51feb1 100644
--- a/platform/macos/sdk/es.lproj/Localizable.strings
+++ b/platform/macos/sdk/es.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "No se pudo cargar el mapa debido a un error desconocido.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "No se pudo cargar el mapa debido a un error de carga en el estilo.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "No se pudo cargar el mapa debido a que el estilo está dañado.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "No se pudo cargar el mapa debido a que no se encuentra el estilo o está incompleto.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/fr.lproj/Localizable.strings b/platform/macos/sdk/fr.lproj/Localizable.strings
index 34e085ef2b..9d73f23d05 100644
--- a/platform/macos/sdk/fr.lproj/Localizable.strings
+++ b/platform/macos/sdk/fr.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* 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_STYLE_FAILED_DESC" = "La carte n’a pas pu être chargée car le style ne peut pas ê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.";
+
+/* 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.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/hu.lproj/Localizable.strings b/platform/macos/sdk/hu.lproj/Localizable.strings
new file mode 100644
index 0000000000..b3724190cb
--- /dev/null
+++ b/platform/macos/sdk/hu.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* 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.";
+
+/* Accessibility title */
+"MAP_A11Y_TITLE" = "Mapbox";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílus hibás.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Nem sikerült betölteni a térképet, mert a stílus nem található vagy inkompatibilis.";
+
+/* Label of Zoom In button */
+"ZOOM_IN_LABEL" = "+";
+
+/* Tooltip of Zoom In button */
+"ZOOM_IN_TOOLTIP" = "Nagyítás";
+
+/* Label of Zoom Out button; U+2212 MINUS SIGN */
+"ZOOM_OUT_LABEL" = "−";
+
+/* Tooltip of Zoom Out button */
+"ZOOM_OUT_TOOLTIP" = "Kicsinyítés";
+
diff --git a/platform/macos/sdk/pt-BR.lproj/Localizable.strings b/platform/macos/sdk/pt-BR.lproj/Localizable.strings
index c7490ec8d8..72fa43b657 100644
--- a/platform/macos/sdk/pt-BR.lproj/Localizable.strings
+++ b/platform/macos/sdk/pt-BR.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Falha ao carregar mapa devido a um erro desconhecido.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Falha ao carregar mapa porque o estilo não pode ser carregado.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Falha ao carregar mapa porque o estilo está corrompido.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Falha ao carregar mapa porque o estilo não pode ser encontrado ou é incompatível.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/ru.lproj/Localizable.strings b/platform/macos/sdk/ru.lproj/Localizable.strings
index 4fa1104cc7..067e05b14e 100644
--- a/platform/macos/sdk/ru.lproj/Localizable.strings
+++ b/platform/macos/sdk/ru.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* 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" = "+";
diff --git a/platform/macos/sdk/uk.lproj/Localizable.strings b/platform/macos/sdk/uk.lproj/Localizable.strings
index 1be17a4d49..07782e81d5 100644
--- a/platform/macos/sdk/uk.lproj/Localizable.strings
+++ b/platform/macos/sdk/uk.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* 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" = "+";
diff --git a/platform/macos/sdk/vi.lproj/Localizable.strings b/platform/macos/sdk/vi.lproj/Localizable.strings
index 086820b034..95e327d689 100644
--- a/platform/macos/sdk/vi.lproj/Localizable.strings
+++ b/platform/macos/sdk/vi.lproj/Localizable.strings
@@ -1,6 +1,18 @@
-/* Accessibility title */
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Bản đồ bị thất bại khi tải vì lỗi không rõ.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Bản đồ bị thất bại khi tải vì không thể tải bảng kiểu.";
+
+/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Bản đồ bị thất bại khi tải vì bảng kiểu bị hỏng.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Bản đồ bị thất bại khi tải vì không tìm thấy bảng kiểu hoặc bảng kiểu không tương thích.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/sdk/zh-Hant.lproj/Localizable.strings b/platform/macos/sdk/zh-Hant.lproj/Localizable.strings
index 4447371d71..e5cf4b7695 100644
--- a/platform/macos/sdk/zh-Hant.lproj/Localizable.strings
+++ b/platform/macos/sdk/zh-Hant.lproj/Localizable.strings
@@ -1,6 +1,18 @@
+/* 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" = "+";
diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h
index 6bfdcfd100..de099157c8 100644
--- a/platform/macos/src/MGLMapView.h
+++ b/platform/macos/src/MGLMapView.h
@@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
@class MGLAnnotationImage;
@class MGLMapCamera;
@class MGLStyle;
+@class MGLShape;
@protocol MGLAnnotation;
@protocol MGLMapViewDelegate;
@@ -239,7 +240,7 @@ MGL_EXPORT IB_DESIGNABLE
The default value of this property is 0.
*/
-@property (nonatomic) double minimumZoomLevel;
+@property (nonatomic) IBInspectable double minimumZoomLevel;
/**
The maximum zoom level the map can be shown at.
@@ -247,9 +248,10 @@ MGL_EXPORT IB_DESIGNABLE
If the value of this property is smaller than that of the `minimumZoomLevel`
property, the behavior is undefined.
- The default value of this property is 20.
+ The default value of this property is 22. The upper bound for this property
+ is 25.5.
*/
-@property (nonatomic) double maximumZoomLevel;
+@property (nonatomic) IBInspectable double maximumZoomLevel;
/**
Changes the zoom level of the map and optionally animates the change.
@@ -321,6 +323,24 @@ MGL_EXPORT IB_DESIGNABLE
*/
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion;
+ /**
+ Moves the viewpoint to a different location with respect to the map with an
+ optional transition duration and timing function.
+
+ @param camera The new viewpoint.
+ @param duration The amount of time, measured in seconds, that the transition
+ animation should take. Specify `0` to jump to the new viewpoint
+ instantaneously.
+ @param function A timing function used for the animation. Set this parameter to
+ `nil` for a transition that matches most system animations. If the duration
+ is `0`, this parameter is ignored.
+ @param edgePadding The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @param completion The block to execute after the animation finishes.
+ */
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(NSEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion;
+
+
/**
Moves the viewpoint to a different location using a transition animation that
evokes powered flight and a default duration based on the length of the flight
@@ -456,6 +476,20 @@ MGL_EXPORT IB_DESIGNABLE
- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets;
/**
+ Returns the camera that best fits the given shape, with the specified direction,
+ optionally with some additional padding on each side.
+
+ @param shape The shape to fit to the receiver’s viewport.
+ @param direction The direction of the viewport, measured in degrees clockwise from true north.
+ @param insets The minimum padding (in screen points) that would be visible
+ around the returned camera object if it were set as the receiver’s camera.
+ @return A camera object centered on the shape's center with zoom level as high
+ (close to the ground) as possible while still including the entire shape. The
+ camera object uses the current pitch.
+ */
+- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(NSEdgeInsets)insets;
+
+/**
A Boolean value indicating whether the receiver automatically adjusts its
content insets.
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index 0a567a8510..b32edffd43 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -4,6 +4,8 @@
#import "MGLCompassCell.h"
#import "MGLOpenGLLayer.h"
#import "MGLStyle.h"
+#import "MGLRendererFrontend.h"
+#import "MGLRendererConfiguration.h"
#import "MGLAnnotationImage_Private.h"
#import "MGLAttributionInfo_Private.h"
@@ -13,6 +15,7 @@
#import "MGLMultiPoint_Private.h"
#import "MGLOfflineStorage_Private.h"
#import "MGLStyle_Private.h"
+#import "MGLShape_Private.h"
#import "MGLAccountManager.h"
#import "MGLMapCamera.h"
@@ -23,15 +26,15 @@
#import "MGLImageSource.h"
#import <mbgl/map/map.hpp>
-#import <mbgl/map/view.hpp>
#import <mbgl/style/style.hpp>
#import <mbgl/annotation/annotation.hpp>
#import <mbgl/map/camera.hpp>
#import <mbgl/storage/reachability.h>
#import <mbgl/util/default_thread_pool.hpp>
-#import <mbgl/map/backend.hpp>
-#import <mbgl/map/backend_scope.hpp>
#import <mbgl/style/image.hpp>
+#import <mbgl/renderer/renderer.hpp>
+#import <mbgl/renderer/renderer_backend.hpp>
+#import <mbgl/renderer/backend_scope.hpp>
#import <mbgl/storage/default_file_source.hpp>
#import <mbgl/storage/network_status.hpp>
#import <mbgl/math/wrap.hpp>
@@ -155,6 +158,7 @@ public:
/// Cross-platform map view controller.
mbgl::Map *_mbglMap;
MGLMapViewImpl *_mbglView;
+ std::unique_ptr<MGLRenderFrontend> _rendererFrontend;
std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool;
NSPanGestureRecognizer *_panGestureRecognizer;
@@ -267,11 +271,12 @@ public:
NSURL *legacyCacheURL = [cachesDirectoryURL URLByAppendingPathComponent:@"cache.db"];
[[NSFileManager defaultManager] removeItemAtURL:legacyCacheURL error:NULL];
- mbgl::DefaultFileSource* mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
-
_mbglThreadPool = mbgl::sharedThreadPool();
- _mbglMap = new mbgl::Map(*_mbglView, self.size, [NSScreen mainScreen].backingScaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
- [self validateTileCacheSize];
+ MGLRendererConfiguration *config = [MGLRendererConfiguration currentConfiguration];
+
+ auto renderer = std::make_unique<mbgl::Renderer>(*_mbglView, config.scaleFactor, *config.fileSource, *_mbglThreadPool, config.contextMode, config.cacheDir, config.localFontFamilyName);
+ _rendererFrontend = std::make_unique<MGLRenderFrontend>(std::move(renderer), self, *_mbglView, true);
+ _mbglMap = new mbgl::Map(*_rendererFrontend, *_mbglView, self.size, config.scaleFactor, *config.fileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
// Install the OpenGL layer. Interface Builder’s synchronous drawing means
// we can’t display a map, so don’t even bother to have a map layer.
@@ -643,6 +648,10 @@ public:
return _mbglMap;
}
+- (mbgl::Renderer *)renderer {
+ return _rendererFrontend->getRenderer();
+}
+
#pragma mark View hierarchy and drawing
- (void)viewWillMoveToWindow:(NSWindow *)newWindow {
@@ -687,9 +696,6 @@ public:
- (void)setFrame:(NSRect)frame {
super.frame = frame;
- if (!NSEqualRects(frame, self.frame)) {
- [self validateTileCacheSize];
- }
if (!_isTargetingInterfaceBuilder) {
_mbglMap->setSize(self.size);
}
@@ -773,11 +779,8 @@ public:
}
- (void)renderSync {
- if (!self.dormant) {
- // The OpenGL implementation automatically enables the OpenGL context for us.
- mbgl::BackendScope scope { *_mbglView, mbgl::BackendScope::ScopeType::Implicit };
-
- _mbglMap->render(*_mbglView);
+ if (!self.dormant && _rendererFrontend) {
+ _rendererFrontend->render();
if (_isPrinting) {
_isPrinting = NO;
@@ -789,21 +792,6 @@ public:
}
}
-- (void)validateTileCacheSize {
- if (!_mbglMap) {
- return;
- }
-
- CGFloat zoomFactor = self.maximumZoomLevel - self.minimumZoomLevel + 1;
- CGFloat cpuFactor = [NSProcessInfo processInfo].processorCount;
- CGFloat memoryFactor = (CGFloat)[NSProcessInfo processInfo].physicalMemory / 1000 / 1000 / 1000;
- CGFloat sizeFactor = (NSWidth(self.bounds) / mbgl::util::tileSize) * (NSHeight(self.bounds) / mbgl::util::tileSize);
-
- NSUInteger cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5;
-
- _mbglMap->setSourceTileCacheSize(cacheSize);
-}
-
- (void)setNeedsGLDisplay {
MGLAssertIsMainThread();
@@ -1061,13 +1049,11 @@ public:
- (void)setMinimumZoomLevel:(double)minimumZoomLevel
{
_mbglMap->setMinZoom(minimumZoomLevel);
- [self validateTileCacheSize];
}
- (void)setMaximumZoomLevel:(double)maximumZoomLevel
{
_mbglMap->setMaxZoom(maximumZoomLevel);
- [self validateTileCacheSize];
}
- (double)maximumZoomLevel {
@@ -1136,6 +1122,10 @@ public:
}
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function completionHandler:(nullable void (^)(void))completion {
+ [self setCamera:camera withDuration:duration animationTimingFunction:function edgePadding:self.contentInsets completionHandler:completion];
+}
+
+- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function edgePadding:(NSEdgeInsets)edgePadding completionHandler:(nullable void (^)(void))completion {
mbgl::AnimationOptions animationOptions;
if (duration > 0) {
animationOptions.duration.emplace(MGLDurationFromTimeInterval(duration));
@@ -1163,7 +1153,7 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
- mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera];
+ mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:edgePadding];
_mbglMap->easeTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
}
@@ -1209,17 +1199,17 @@ public:
[self willChangeValueForKey:@"camera"];
_mbglMap->cancelTransitions();
- mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera];
+ mbgl::CameraOptions cameraOptions = [self cameraOptionsObjectForAnimatingToCamera:camera edgePadding:self.contentInsets];
_mbglMap->flyTo(cameraOptions, animationOptions);
[self didChangeValueForKey:@"camera"];
}
/// Returns a CameraOptions object that specifies parameters for animating to
/// the given camera.
-- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera {
+- (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera edgePadding:(NSEdgeInsets) edgePadding {
mbgl::CameraOptions options;
options.center = MGLLatLngFromLocationCoordinate2D(camera.centerCoordinate);
- options.padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets);
+ options.padding = MGLEdgeInsetsFromNSEdgeInsets(edgePadding);
options.zoom = MGLZoomLevelForAltitude(camera.altitude, camera.pitch,
camera.centerCoordinate.latitude,
self.frame.size);
@@ -1282,6 +1272,15 @@ public:
return [self cameraForCameraOptions:cameraOptions];
}
+- (MGLMapCamera *)cameraThatFitsShape:(MGLShape *)shape direction:(CLLocationDirection)direction edgePadding:(NSEdgeInsets)insets {
+ mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets);
+ padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets);
+
+ mbgl::CameraOptions cameraOptions = _mbglMap->cameraForGeometry([shape geometryObject], padding, direction);
+
+ return [self cameraForCameraOptions:cameraOptions];
+}
+
- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions {
CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng());
double zoomLevel = cameraOptions.zoom ? *cameraOptions.zoom : self.zoomLevel;
@@ -1374,7 +1373,7 @@ public:
// Move the cursor back to the start point and show it again, creating
// the illusion that it has stayed in place during the entire gesture.
CGPoint cursorPoint = [self convertPoint:startPoint toView:nil];
- cursorPoint = [self.window convertRectToScreen:{ startPoint, NSZeroSize }].origin;
+ cursorPoint = [self.window convertRectToScreen:{ cursorPoint, NSZeroSize }].origin;
cursorPoint.y = self.window.screen.frame.size.height - cursorPoint.y;
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cursorPoint);
CGDisplayShowCursor(kCGDirectMainDisplay);
@@ -1489,7 +1488,7 @@ public:
if (hitAnnotationTag != _selectedAnnotationTag) {
id <MGLAnnotation> annotation = [self annotationWithTag:hitAnnotationTag];
NSAssert(annotation, @"Cannot select nonexistent annotation with tag %u", hitAnnotationTag);
- [self selectAnnotation:annotation];
+ [self selectAnnotation:annotation atPoint:gesturePoint];
}
} else {
[self deselectAnnotation:self.selectedAnnotation];
@@ -1792,6 +1791,12 @@ public:
}
std::vector<MGLAnnotationTag> annotationTags = [self annotationTagsInRect:rect];
+ std::vector<MGLAnnotationTag> shapeAnnotationTags = [self shapeAnnotationTagsInRect:rect];
+
+ if (shapeAnnotationTags.size()) {
+ annotationTags.insert(annotationTags.end(), shapeAnnotationTags.begin(), shapeAnnotationTags.end());
+ }
+
if (annotationTags.size())
{
NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()];
@@ -2051,13 +2056,18 @@ public:
queryRect = NSInsetRect(queryRect, -MGLAnnotationImagePaddingForHitTest,
-MGLAnnotationImagePaddingForHitTest);
std::vector<MGLAnnotationTag> nearbyAnnotations = [self annotationTagsInRect:queryRect];
+ std::vector<MGLAnnotationTag> nearbyShapeAnnotations = [self shapeAnnotationTagsInRect:queryRect];
+
+ if (nearbyShapeAnnotations.size()) {
+ nearbyAnnotations.insert(nearbyAnnotations.end(), nearbyShapeAnnotations.begin(), nearbyShapeAnnotations.end());
+ }
if (nearbyAnnotations.size()) {
// Assume that the user is fat-fingering an annotation.
NSRect hitRect = NSInsetRect({ point, NSZeroSize },
-MGLAnnotationImagePaddingForHitTest,
-MGLAnnotationImagePaddingForHitTest);
-
+
// Filter out any annotation whose image is unselectable or for which
// hit testing fails.
auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag annotationTag) {
@@ -2066,12 +2076,17 @@ public:
if (!annotation) {
return true;
}
-
+
+ if ([annotation isKindOfClass:[MGLShape class]])
+ {
+ return false;
+ }
+
MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
if (!annotationImage.selectable) {
return true;
}
-
+
// Filter out the annotation if the fattened finger didn’t land on a
// translucent or opaque pixel in the image.
NSRect annotationRect = [self frameOfImage:annotationImage.image
@@ -2144,7 +2159,15 @@ public:
/// Returns the tags of the annotations coincident with the given rectangle.
- (std::vector<MGLAnnotationTag>)annotationTagsInRect:(NSRect)rect {
// Cocoa origin is at the lower-left corner.
- return _mbglMap->queryPointAnnotations({
+ return self.renderer->queryPointAnnotations({
+ { NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) },
+ { NSMaxX(rect), NSHeight(self.bounds) - NSMinY(rect) },
+ });
+}
+
+- (std::vector<MGLAnnotationTag>)shapeAnnotationTagsInRect:(NSRect)rect {
+ // Cocoa origin is at the lower-left corner.
+ return _rendererFrontend->getRenderer()->queryShapeAnnotations({
{ NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) },
{ NSMaxX(rect), NSHeight(self.bounds) - NSMinY(rect) },
});
@@ -2193,11 +2216,11 @@ public:
- (void)selectAnnotation:(id <MGLAnnotation>)annotation
{
- // Only point annotations can be selected.
- if (!annotation || [annotation isKindOfClass:[MGLMultiPoint class]]) {
- return;
- }
+ [self selectAnnotation:annotation atPoint:NSZeroPoint];
+}
+- (void)selectAnnotation:(id <MGLAnnotation>)annotation atPoint:(NSPoint)gesturePoint
+{
id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
if (annotation == selectedAnnotation) {
return;
@@ -2212,10 +2235,10 @@ public:
[self addAnnotation:annotation];
}
- // The annotation can’t be selected if no part of it is hittable.
+ // The annotation's anchor will bounce to the current click.
NSRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag];
if (NSIsEmptyRect(NSIntersectionRect(positioningRect, self.bounds))) {
- return;
+ positioningRect = CGRectMake(gesturePoint.x, gesturePoint.y, positioningRect.size.width, positioningRect.size.height);
}
self.selectedAnnotation = annotation;
@@ -2315,6 +2338,13 @@ public:
if (!annotation) {
return NSZeroRect;
}
+ if ([annotation isKindOfClass:[MGLMultiPoint class]]) {
+ CLLocationCoordinate2D origin = annotation.coordinate;
+ CGPoint originPoint = [self convertCoordinate:origin toPointToView:self];
+ return CGRectMake(originPoint.x, originPoint.y, MGLAnnotationImagePaddingForHitTest, MGLAnnotationImagePaddingForHitTest);
+
+ }
+
NSImage *image = [self imageOfAnnotationWithTag:annotationTag].image;
if (!image) {
image = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName].image;
@@ -2565,7 +2595,7 @@ public:
optionalFilter = predicate.mgl_filter;
}
- std::vector<mbgl::Feature> features = _mbglMap->queryRenderedFeatures(screenCoordinate, { optionalLayerIDs, optionalFilter });
+ std::vector<mbgl::Feature> features = _rendererFrontend->getRenderer()->queryRenderedFeatures(screenCoordinate, { optionalLayerIDs, optionalFilter });
return MGLFeaturesFromMBGLFeatures(features);
}
@@ -2599,7 +2629,7 @@ public:
optionalFilter = predicate.mgl_filter;
}
- std::vector<mbgl::Feature> features = _mbglMap->queryRenderedFeatures(screenBox, { optionalLayerIDs, optionalFilter });
+ std::vector<mbgl::Feature> features = _rendererFrontend->getRenderer()->queryRenderedFeatures(screenBox, { optionalLayerIDs, optionalFilter });
return MGLFeaturesFromMBGLFeatures(features);
}
@@ -2777,10 +2807,9 @@ public:
}
/// Adapter responsible for bridging calls from mbgl to MGLMapView and Cocoa.
-class MGLMapViewImpl : public mbgl::View, public mbgl::Backend {
+class MGLMapViewImpl : public mbgl::RendererBackend, public mbgl::MapObserver {
public:
- MGLMapViewImpl(MGLMapView *nativeView_)
- : nativeView(nativeView_) {}
+ MGLMapViewImpl(MGLMapView *nativeView_) : nativeView(nativeView_) {}
void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override {
bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated;
@@ -2858,7 +2887,7 @@ public:
[nativeView sourceDidChange:nativeSource];
}
- mbgl::gl::ProcAddress initializeExtension(const char* name) override {
+ mbgl::gl::ProcAddress getExtensionFunctionPointer(const char* name) override {
static CFBundleRef framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
if (!framework) {
throw std::runtime_error("Failed to load OpenGL framework.");
@@ -2871,10 +2900,6 @@ public:
return reinterpret_cast<mbgl::gl::ProcAddress>(symbol);
}
- void invalidate() override {
- [nativeView setNeedsGLDisplay];
- }
-
void activate() override {
if (activationCount++) {
return;
@@ -2903,6 +2928,10 @@ public:
setViewport(0, 0, nativeView.framebufferSize);
}
+ mbgl::Size getFramebufferSize() const override {
+ return nativeView.framebufferSize;
+ }
+
mbgl::PremultipliedImage readStillImage() {
return readFramebuffer(nativeView.framebufferSize);
}
diff --git a/platform/macos/src/MGLMapView_Private.h b/platform/macos/src/MGLMapView_Private.h
index 5ac75768a1..f2d178bc31 100644
--- a/platform/macos/src/MGLMapView_Private.h
+++ b/platform/macos/src/MGLMapView_Private.h
@@ -2,6 +2,7 @@
namespace mbgl {
class Map;
+ class Renderer;
}
@interface MGLMapView (Private)
@@ -29,4 +30,6 @@ namespace mbgl {
- (mbgl::Map *)mbglMap;
+- (mbgl::Renderer *)renderer;
+
@end
diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h
index e4ad258b6e..26c67d5550 100644
--- a/platform/macos/src/Mapbox.h
+++ b/platform/macos/src/Mapbox.h
@@ -49,6 +49,8 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLTileSource.h"
#import "MGLVectorSource.h"
#import "MGLShapeSource.h"
+#import "MGLAbstractShapeSource.h"
+#import "MGLComputedShapeSource.h"
#import "MGLRasterSource.h"
#import "MGLImageSource.h"
#import "MGLTilePyramidOfflineRegion.h"
@@ -56,3 +58,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "NSValue+MGLAdditions.h"
#import "MGLStyleValue.h"
#import "MGLAttributionInfo.h"
+#import "MGLMapSnapshotter.h"
diff --git a/platform/macos/src/NSImage+MGLAdditions.mm b/platform/macos/src/NSImage+MGLAdditions.mm
index 6abe53e9ae..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;
}
@@ -42,15 +42,8 @@
}
- (mbgl::PremultipliedImage)mgl_premultipliedImage {
- // Create a bitmap image representation from the image, respecting backing
- // scale factor and any resizing done on the image at runtime.
- // http://www.cocoabuilder.com/archive/cocoa/82430-nsimage-getting-raw-bitmap-data.html#82431
- [self lockFocus];
- NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:{ NSZeroPoint, self.size }];
- [self unlockFocus];
-
- mbgl::PremultipliedImage cPremultipliedImage({ static_cast<uint32_t>(rep.pixelsWide), static_cast<uint32_t>(rep.pixelsHigh) });
- std::copy(rep.bitmapData, rep.bitmapData + cPremultipliedImage.bytes(), cPremultipliedImage.data.get());
- return cPremultipliedImage;
+ CGImageRef ref = [self CGImageForProposedRect:nullptr context:nullptr hints:nullptr];
+ return MGLPremultipliedImageFromCGImage(ref);
}
+
@end
diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md
index dee001c426..feb2b4185d 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,3 +1,23 @@
+# 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)
+- Fixed an issue in test [#9553](https://github.com/mapbox/mapbox-gl-native/pull/9553)
+- Increased the default maximum zoom level from 20 to 22 ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+
+# 3.5.5 - July 14, 2017
+- Provide debuggable release builds for node packages [#9497](https://github.com/mapbox/mapbox-gl-native/pull/9497)
+
# 3.5.4 - June 6, 2017
- Add support for ImageSource [#8968](https://github.com/mapbox/mapbox-gl-native/pull/8968)
- Fixed an issue with `map.addImage()` which would cause added images to randomly be replaced with images found the style's sprite sheet ([#9119](https://github.com/mapbox/mapbox-gl-native/pull/9119))
@@ -176,7 +196,7 @@
- Check libuv version semver-ishly, fixes segfaults in Node.js 0.12.x
and io.js.
-- Fixes segfault, throws JavaScript error instead when attempting to
+- Fixes segfault, throws JavaScript error instead when attempting to
render without first loading a style.
- Bumps mbgl submodule to v0.4.0
@@ -202,12 +222,12 @@
# 1.0.3 - April 3, 2015
-- Fixes crash during garbage collection by assigning FileSource handle
+- Fixes crash during garbage collection by assigning FileSource handle
to a v8::Persistent in NodeMap constructor.
# 1.0.2 - April 2, 2015
-- Initialize shared display connection at module load time to avoid
+- Initialize shared display connection at module load time to avoid
race condition when display connection is initialized on-demand.
# 1.0.1 - March 19, 2015
diff --git a/platform/node/DEVELOPING.md b/platform/node/DEVELOPING.md
index 4e8da881a8..b313d75c13 100644
--- a/platform/node/DEVELOPING.md
+++ b/platform/node/DEVELOPING.md
@@ -1,6 +1,6 @@
# Developing the Mapbox GL Native Node.js module
-This document explains how to build the [Node.js](https://nodejs.org/) bindings for [../../README.md](Mapbox GL Native) for contributing to the development of the bindings themselves. If you just want to use the module, you can simply install it via `npm`; see [README.md](README.md) for installation and usage instructions.
+This document explains how to build the [Node.js](https://nodejs.org/) bindings for [Mapbox GL Native](../../README.md) for contributing to the development of the bindings themselves. If you just want to use the module, you can simply install it via `npm`; see [README.md](README.md) for installation and usage instructions.
## Building
diff --git a/platform/node/bitrise.yml b/platform/node/bitrise.yml
index 00005f0f36..4a013ea8b0 100644
--- a/platform/node/bitrise.yml
+++ b/platform/node/bitrise.yml
@@ -1,74 +1,15 @@
-format_version: 1.3.0
+format_version: 1.1.0
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
trigger_map:
-- tag: "node-v*"
- workflow: publish
-- push_branch: "*"
+- pattern: "*"
+ is_pull_request_allowed: true
workflow: primary
-- pull_request_target_branch: "*"
- workflow: primary
-
-shortcuts:
- slack: &slack
- title: Post to Slack
- inputs:
- - webhook_url: "$SLACK_HOOK_URL"
- - channel: "#gl-bots"
- - from_username: 'Bitrise Node macOS'
- - from_username_on_error: 'Bitrise Node macOS'
- - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- passed'
- - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- failed'
- - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
workflows:
primary:
steps:
- script:
- title: Test
- inputs:
- - content: |-
- #!/bin/bash
- set -eu -o pipefail
- brew update
- brew unlink node
- brew install cmake awscli node@4 node@6
- brew link node@4 --force
- gem install xcpretty --no-rdoc --no-ri
- make test-node || RESULT=$?
- brew unlink node@4
- brew link --overwrite node@6 --force
- make clean
- make test-node || RESULT=$?
- ./platform/node/scripts/after_script.sh ${BITRISE_BUILD_NUMBER}
- exit ${RESULT:-0}
- - slack: *slack
-
- publish:
- steps:
- - script:
- title: Publish
+ title: Skip Workflow
inputs:
- - content: |-
- #!/bin/bash
- set -eu -o pipefail
- brew update
- brew unlink node
- brew install cmake awscli node@4 node@6
- brew link node@4 --force
- gem install xcpretty --no-rdoc --no-ri
- export BUILDTYPE=Release
- export PUBLISH=true
- make test-node && ./platform/node/scripts/after_success.sh
- brew unlink node@4
- brew link --overwrite node@6 --force
- make clean
- make test-node && ./platform/node/scripts/after_success.sh
- - slack: *slack
+ - content: echo "This workflow is obsolete — see CircleCi."
diff --git a/platform/node/index.js b/platform/node/index.js
index 54ba5c0dc6..5944a0a27d 100644
--- a/platform/node/index.js
+++ b/platform/node/index.js
@@ -4,6 +4,7 @@
var mbgl = require('../../lib/mapbox_gl_native.node');
var constructor = mbgl.Map.prototype.constructor;
+var process = require('process');
var Map = function(options) {
if (!(options instanceof Object)) {
@@ -18,9 +19,29 @@ var Map = function(options) {
return new constructor(Object.assign(options, {
request: function(req) {
- request(req, function() {
- req.respond.apply(req, arguments);
- });
+ // Protect against `request` implementations that call the callback synchronously,
+ // call it multiple times, or throw exceptions.
+ // http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
+
+ var responded = false;
+ var callback = function() {
+ var args = arguments;
+ if (!responded) {
+ responded = true;
+ process.nextTick(function() {
+ req.respond.apply(req, args);
+ });
+ } else {
+ console.warn('request function responded multiple times; it should call the callback only once');
+ }
+ };
+
+ try {
+ request(req, callback);
+ } catch (e) {
+ console.warn('request function threw an exception; it should call the callback with an error instead');
+ callback(e);
+ }
}
}));
};
diff --git a/platform/node/scripts/after_script.sh b/platform/node/scripts/after_script.sh
deleted file mode 100755
index beb8d6966f..0000000000
--- a/platform/node/scripts/after_script.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-set -e
-set -o pipefail
-
-JOB=$1
-
-if [ ! -z "${AWS_ACCESS_KEY_ID}" ] && [ ! -z "${AWS_SECRET_ACCESS_KEY}" ] ; then
- gzip --stdout mapbox-gl-js/test/integration/render-tests/index.html | \
- aws s3 cp --acl public-read --content-encoding gzip --content-type text/html \
- - s3://mapbox/mapbox-gl-native/render-tests/$JOB/index.html
-
- echo http://mapbox.s3.amazonaws.com/mapbox-gl-native/render-tests/$JOB/index.html
-fi
diff --git a/platform/node/scripts/after_success.sh b/platform/node/scripts/after_success.sh
index a050dbce07..a5c3c5ec36 100755
--- a/platform/node/scripts/after_success.sh
+++ b/platform/node/scripts/after_success.sh
@@ -3,10 +3,15 @@
set -e
set -o pipefail
-if [[ "${PUBLISH:-}" == "true" ]]; then
- if [[ "${BUILDTYPE}" == "Release" ]]; then
+PACKAGE_JSON_VERSION=`node -e "console.log(require('./package.json').version)"`
+
+if [[ "${CIRCLE_TAG}" == "node-v${PACKAGE_JSON_VERSION}" ]] || [[ "${PUBLISH:-}" == true ]]; then
+ if [[ "${BUILDTYPE}" == "RelWithDebInfo" ]]; then
./node_modules/.bin/node-pre-gyp package publish info
- else
+ elif [[ "${BUILDTYPE}" == "Debug" ]]; then
./node_modules/.bin/node-pre-gyp package publish info --debug
+ else
+ echo "error: must provide either Debug or RelWithDebInfo for BUILDTYPE"
+ exit 1
fi
fi
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..8958d5c6c7
--- /dev/null
+++ b/platform/node/src/node_expression.cpp
@@ -0,0 +1,230 @@
+#include "node_conversion.hpp"
+#include "node_expression.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::SetMethod(tpl, "parse", Parse);
+
+ constructor.Reset(tpl->GetFunction()); // what is this doing?
+ Nan::Set(target, Nan::New("Expression").ToLocalChecked(), tpl->GetFunction());
+}
+
+type::Type parseType(v8::Local<v8::Object> type) {
+ static std::unordered_map<std::string, type::Type> types = {
+ {"string", type::String},
+ {"number", type::Number},
+ {"noolean", type::Boolean},
+ {"object", type::Object},
+ {"color", type::Color},
+ {"value", type::Value}
+ };
+
+ v8::Local<v8::Value> v8kind = Nan::Get(type, Nan::New("kind").ToLocalChecked()).ToLocalChecked();
+ std::string kind(*v8::String::Utf8Value(v8kind));
+
+ if (kind == "array") {
+ type::Type itemType = parseType(Nan::Get(type, Nan::New("itemType").ToLocalChecked()).ToLocalChecked()->ToObject());
+ mbgl::optional<std::size_t> N;
+
+ v8::Local<v8::String> Nkey = Nan::New("N").ToLocalChecked();
+ if (Nan::Has(type, Nkey).FromMaybe(false)) {
+ N = Nan::Get(type, Nkey).ToLocalChecked()->ToInt32()->Value();
+ }
+ return type::Array(itemType, N);
+ }
+
+ return types[kind];
+}
+
+void NodeExpression::Parse(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ v8::Local<v8::Function> cons = Nan::New(constructor);
+
+ if (info.Length() < 1 || info[0]->IsUndefined()) {
+ return Nan::ThrowTypeError("Requires a JSON style expression argument.");
+ }
+
+ mbgl::optional<type::Type> expected;
+ if (info.Length() > 1 && info[1]->IsObject()) {
+ expected = parseType(info[1]->ToObject());
+ }
+
+ auto expr = info[0];
+
+ try {
+ ParsingContext ctx(expected);
+ ParseResult parsed = ctx.parse(mbgl::style::conversion::Convertible(expr));
+ if (parsed) {
+ assert(ctx.getErrors().size() == 0);
+ auto nodeExpr = new NodeExpression(std::move(*parsed));
+ const int argc = 0;
+ v8::Local<v8::Value> argv[0] = {};
+ auto wrapped = Nan::NewInstance(cons, argc, argv).ToLocalChecked();
+ nodeExpr->Wrap(wrapped);
+ info.GetReturnValue().Set(wrapped);
+ return;
+ }
+
+ v8::Local<v8::Array> result = Nan::New<v8::Array>();
+ for (std::size_t i = 0; i < ctx.getErrors().size(); i++) {
+ const auto& error = ctx.getErrors()[i];
+ v8::Local<v8::Object> err = Nan::New<v8::Object>();
+ Nan::Set(err,
+ Nan::New("key").ToLocalChecked(),
+ Nan::New(error.key.c_str()).ToLocalChecked());
+ Nan::Set(err,
+ Nan::New("error").ToLocalChecked(),
+ Nan::New(error.message.c_str()).ToLocalChecked());
+ Nan::Set(result, Nan::New((uint32_t)i), err);
+ }
+ info.GetReturnValue().Set(result);
+ } catch(std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+}
+
+void NodeExpression::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ if (!info.IsConstructCall()) {
+ return Nan::ThrowTypeError("Use the new operator to create new Expression objects");
+ }
+
+ info.GetReturnValue().Set(info.This());
+}
+
+struct ToValue {
+ v8::Local<v8::Value> operator()(mbgl::NullValue) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::Null());
+ }
+
+ v8::Local<v8::Value> operator()(bool t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t));
+ }
+
+ v8::Local<v8::Value> operator()(double t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t));
+ }
+
+ v8::Local<v8::Value> operator()(const std::string& t) {
+ Nan::EscapableHandleScope scope;
+ return scope.Escape(Nan::New(t).ToLocalChecked());
+ }
+
+ v8::Local<v8::Value> operator()(const std::vector<Value>& array) {
+ Nan::EscapableHandleScope scope;
+ v8::Local<v8::Array> result = Nan::New<v8::Array>();
+ for (unsigned int i = 0; i < array.size(); i++) {
+ result->Set(i, toJS(array[i]));
+ }
+ return scope.Escape(result);
+ }
+
+ v8::Local<v8::Value> operator()(const mbgl::Color& color) {
+ return operator()(std::vector<Value> {
+ static_cast<double>(color.r),
+ static_cast<double>(color.g),
+ static_cast<double>(color.b),
+ static_cast<double>(color.a)
+ });
+ }
+
+ v8::Local<v8::Value> operator()(const std::unordered_map<std::string, Value>& map) {
+ Nan::EscapableHandleScope scope;
+ v8::Local<v8::Object> result = Nan::New<v8::Object>();
+ for (const auto& entry : map) {
+ Nan::Set(result, Nan::New(entry.first).ToLocalChecked(), toJS(entry.second));
+ }
+
+ return scope.Escape(result);
+ }
+};
+
+v8::Local<v8::Value> toJS(const Value& value) {
+ return Value::visit(value, ToValue());
+}
+
+void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+
+ if (info.Length() < 2 || !info[0]->IsObject()) {
+ return Nan::ThrowTypeError("Requires globals and feature arguments.");
+ }
+
+ mbgl::optional<float> zoom;
+ v8::Local<v8::Value> v8zoom = Nan::Get(info[0]->ToObject(), Nan::New("zoom").ToLocalChecked()).ToLocalChecked();
+ if (v8zoom->IsNumber()) zoom = v8zoom->NumberValue();
+
+ mbgl::optional<double> heatmapDensity;
+ v8::Local<v8::Value> v8heatmapDensity = Nan::Get(info[0]->ToObject(), Nan::New("heatmapDensity").ToLocalChecked()).ToLocalChecked();
+ if (v8heatmapDensity->IsNumber()) heatmapDensity = v8heatmapDensity->NumberValue();
+
+ Nan::JSON NanJSON;
+ conversion::Error conversionError;
+ mbgl::optional<mbgl::GeoJSON> geoJSON = conversion::convert<mbgl::GeoJSON>(info[1], conversionError);
+ if (!geoJSON) {
+ Nan::ThrowTypeError(conversionError.message.c_str());
+ return;
+ }
+
+ try {
+ mapbox::geojson::feature feature = geoJSON->get<mapbox::geojson::feature>();
+ auto result = expression->evaluate(zoom, feature, heatmapDensity);
+ if (result) {
+ info.GetReturnValue().Set(toJS(*result));
+ } else {
+ v8::Local<v8::Object> res = Nan::New<v8::Object>();
+ Nan::Set(res,
+ Nan::New("error").ToLocalChecked(),
+ Nan::New(result.error().message.c_str()).ToLocalChecked());
+ info.GetReturnValue().Set(res);
+ }
+ } catch(std::exception &ex) {
+ return Nan::ThrowTypeError(ex.what());
+ }
+}
+
+void NodeExpression::GetType(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+
+ const type::Type type = expression->getType();
+ const std::string name = type.match([&] (const auto& t) { return t.getName(); });
+ info.GetReturnValue().Set(Nan::New(name.c_str()).ToLocalChecked());
+}
+
+void NodeExpression::IsFeatureConstant(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+ info.GetReturnValue().Set(Nan::New(isFeatureConstant(*expression)));
+}
+
+void NodeExpression::IsZoomConstant(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
+ const std::unique_ptr<Expression>& expression = nodeExpr->expression;
+ info.GetReturnValue().Set(Nan::New(isZoomConstant(*expression)));
+}
+
+} // 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..7af5b7ab51
--- /dev/null
+++ b/platform/node/src/node_expression.hpp
@@ -0,0 +1,40 @@
+#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 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 9bae86b76a..0000000000
--- a/platform/node/src/node_geojson.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <mbgl/style/conversion/geojson.hpp>
-
-namespace mbgl {
-namespace style {
-namespace conversion {
-
-template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>&, Error& error) const {
- error = { "not implemented" };
- return {};
-}
-
-} // namespace conversion
-} // namespace style
-} // namespace mbgl
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 9ba88193c9..b8c5e9cc88 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -2,17 +2,25 @@
#include "node_request.hpp"
#include "node_feature.hpp"
#include "node_conversion.hpp"
-#include "node_geojson.hpp"
-#include <mbgl/gl/headless_display.hpp>
#include <mbgl/util/exception.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
#include <mbgl/style/conversion/source.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/conversion/filter.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/style.hpp>
#include <mbgl/style/image.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/map/query.hpp>
+#include <mbgl/map/map_observer.hpp>
#include <mbgl/util/premultiply.hpp>
#include <unistd.h>
@@ -25,27 +33,21 @@ struct NodeMap::RenderOptions {
double pitch = 0;
double latitude = 0;
double longitude = 0;
- unsigned int width = 512;
- unsigned int height = 512;
+ 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;
};
Nan::Persistent<v8::Function> NodeMap::constructor;
-static std::shared_ptr<mbgl::HeadlessDisplay> sharedDisplay() {
- static auto display = std::make_shared<mbgl::HeadlessDisplay>();
- return display;
-}
-
static const char* releasedMessage() {
return "Map resources have already been released";
}
-NodeBackend::NodeBackend()
- : HeadlessBackend(sharedDisplay()) {}
-
-void NodeBackend::onDidFailLoadingMap(std::exception_ptr error) {
+void NodeMapObserver::onDidFailLoadingMap(std::exception_ptr error) {
std::rethrow_exception(error);
}
@@ -62,6 +64,7 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
Nan::SetPrototypeMethod(tpl, "addSource", AddSource);
+ Nan::SetPrototypeMethod(tpl, "removeSource", RemoveSource);
Nan::SetPrototypeMethod(tpl, "addLayer", AddLayer);
Nan::SetPrototypeMethod(tpl, "removeLayer", RemoveLayer);
Nan::SetPrototypeMethod(tpl, "addImage", AddImage);
@@ -73,15 +76,15 @@ 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, "setAxonometric", SetAxonometric);
+ Nan::SetPrototypeMethod(tpl, "setXSkew", SetXSkew);
+ Nan::SetPrototypeMethod(tpl, "setYSkew", SetYSkew);
Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs);
Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures);
constructor.Reset(tpl->GetFunction());
Nan::Set(target, Nan::New("Map").ToLocalChecked(), tpl->GetFunction());
-
- // Initialize display connection on module load.
- sharedDisplay();
}
/**
@@ -163,25 +166,15 @@ void NodeMap::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(info.This());
}
-std::string StringifyStyle(v8::Local<v8::Value> styleHandle) {
- Nan::HandleScope scope;
-
- v8::Local<v8::Object> JSON = Nan::To<v8::Object>(
- Nan::Get(
- Nan::GetCurrentContext()->Global(),
- Nan::New("JSON").ToLocalChecked()
- ).ToLocalChecked()
- ).ToLocalChecked();
-
- return *Nan::Utf8String(Nan::MakeCallback(JSON, "stringify", 1, &styleHandle));
-}
-
/**
* Load a stylesheet
*
* @function
* @name load
* @param {string|Object} stylesheet either an object or a JSON representation
+ * @param {Object} options
+ * @param {boolean} options.defaultStyleCamera if true, sets the default style
+ * camera
* @returns {undefined} loads stylesheet into map
* @throws {Error} if stylesheet is missing or invalid
* @example
@@ -206,7 +199,8 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
std::string style;
if (info[0]->IsObject()) {
- style = StringifyStyle(info[0]);
+ Nan::JSON JSON;
+ style = *Nan::Utf8String(JSON.Stringify(info[0]->ToObject()).ToLocalChecked());
} else if (info[0]->IsString()) {
style = *Nan::Utf8String(info[0]);
} else {
@@ -219,6 +213,21 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowError(ex.what());
}
+ if (info.Length() == 2) {
+ if (!info[1]->IsObject()) {
+ return Nan::ThrowTypeError("Second argument must be an options object");
+ }
+ auto options = Nan::To<v8::Object>(info[1]).ToLocalChecked();
+ if (Nan::Has(options, Nan::New("defaultStyleCamera").ToLocalChecked()).FromJust()) {
+ if (!Nan::Get(options, Nan::New("defaultStyleCamera").ToLocalChecked()).ToLocalChecked()->IsBoolean()) {
+ return Nan::ThrowError("Options object 'defaultStyleCamera' property must be a boolean");
+ }
+ if (Nan::Get(options, Nan::New("cameraMutated").ToLocalChecked()).ToLocalChecked()->BooleanValue()) {
+ nodeMap->map->jumpTo(nodeMap->map->getStyle().getDefaultCamera());
+ }
+ }
+ }
+
nodeMap->loaded = true;
info.GetReturnValue().SetUndefined();
@@ -256,6 +265,19 @@ 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("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()) {
@@ -266,11 +288,11 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
}
if (Nan::Has(obj, Nan::New("width").ToLocalChecked()).FromJust()) {
- options.width = Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()->IntegerValue();
+ options.size.width = Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()->IntegerValue();
}
if (Nan::Has(obj, Nan::New("height").ToLocalChecked()).FromJust()) {
- options.height = Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()->IntegerValue();
+ options.size.height = Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()->IntegerValue();
}
if (Nan::Has(obj, Nan::New("classes").ToLocalChecked()).FromJust()) {
@@ -366,44 +388,34 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
}
void NodeMap::startRender(NodeMap::RenderOptions options) {
- map->setSize({ options.width, options.height });
+ frontend->setSize(options.size);
+ map->setSize(options.size);
- const mbgl::Size fbSize{ static_cast<uint32_t>(options.width * pixelRatio),
- static_cast<uint32_t>(options.height * pixelRatio) };
- if (!view || view->getSize() != fbSize) {
- view.reset();
- mbgl::BackendScope scope { backend };
- view = std::make_unique<mbgl::OffscreenView>(backend.getContext(), fbSize);
- }
-
- if (map->getZoom() != options.zoom) {
- map->setZoom(options.zoom);
- }
+ mbgl::CameraOptions camera;
+ camera.center = mbgl::LatLng { options.latitude, options.longitude };
+ camera.zoom = options.zoom;
+ camera.angle = -options.bearing * mbgl::util::DEG2RAD;
+ camera.pitch = options.pitch * mbgl::util::DEG2RAD;
- mbgl::LatLng latLng(options.latitude, options.longitude);
- if (map->getLatLng() != latLng) {
- map->setLatLng(latLng);
+ if (map->getAxonometric() != options.axonometric) {
+ map->setAxonometric(options.axonometric);
}
- if (map->getBearing() != options.bearing) {
- map->setBearing(options.bearing);
+ if (map->getXSkew() != options.xSkew) {
+ map->setXSkew(options.xSkew);
}
- if (map->getPitch() != options.pitch) {
- map->setPitch(options.pitch);
+ if (map->getYSkew() != options.ySkew) {
+ map->setYSkew(options.ySkew);
}
- if (map->getDebug() != options.debugOptions) {
- map->setDebug(options.debugOptions);
- }
-
- map->renderStill(*view, [this](const std::exception_ptr eptr) {
+ map->renderStill(camera, options.debugOptions, [this](const std::exception_ptr eptr) {
if (eptr) {
error = std::move(eptr);
uv_async_send(async);
} else {
assert(!image.data);
- image = view->readStillImage();
+ image = frontend->readStillImage();
uv_async_send(async);
}
});
@@ -502,8 +514,9 @@ void NodeMap::release() {
uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *h) {
delete reinterpret_cast<uv_async_t *>(h);
});
-
+
map.reset();
+ frontend.reset();
}
/**
@@ -529,9 +542,13 @@ void NodeMap::Cancel(const Nan::FunctionCallbackInfo<v8::Value>& info) {
void NodeMap::cancel() {
auto style = map->getStyle().getJSON();
+
+ // Reset map explicitly as it resets the renderer frontend
+ map.reset();
- map = std::make_unique<mbgl::Map>(backend, mbgl::Size{ 256, 256 },
- pixelRatio, *this, threadpool, mbgl::MapMode::Still);
+ 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, 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
@@ -557,6 +574,10 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
+ if (!info[1]->IsObject()) {
+ return Nan::ThrowTypeError("Second argument must be an object");
+ }
+
Error error;
mbgl::optional<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(info[1], error, *Nan::Utf8String(info[0]));
if (!source) {
@@ -567,6 +588,24 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
nodeMap->map->getStyle().addSource(std::move(*source));
}
+void NodeMap::RemoveSource(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() != 1) {
+ return Nan::ThrowTypeError("One argument required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ nodeMap->map->getStyle().removeSource(*Nan::Utf8String(info[0]));
+}
+
void NodeMap::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
using namespace mbgl::style;
using namespace mbgl::style::conversion;
@@ -710,7 +749,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());
}
@@ -742,7 +781,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());
}
@@ -880,11 +919,64 @@ void NodeMap::SetPitch(const Nan::FunctionCallbackInfo<v8::Value>& info) {
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());
nodeMap->map->dumpDebugLogs();
+ nodeMap->frontend->getRenderer()->dumpDebugLogs();
+
info.GetReturnValue().SetUndefined();
}
@@ -946,7 +1038,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
auto pos0 = Nan::Get(posOrBox, 0).ToLocalChecked().As<v8::Array>();
auto pos1 = Nan::Get(posOrBox, 1).ToLocalChecked().As<v8::Array>();
- optional = nodeMap->map->queryRenderedFeatures(mbgl::ScreenBox {
+ optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenBox {
{
Nan::Get(pos0, 0).ToLocalChecked()->NumberValue(),
Nan::Get(pos0, 1).ToLocalChecked()->NumberValue()
@@ -957,7 +1049,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
}, queryOptions);
} else {
- optional = nodeMap->map->queryRenderedFeatures(mbgl::ScreenCoordinate {
+ optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenCoordinate {
Nan::Get(posOrBox, 0).ToLocalChecked()->NumberValue(),
Nan::Get(posOrBox, 1).ToLocalChecked()->NumberValue()
}, queryOptions);
@@ -981,13 +1073,25 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
.ToLocalChecked()
->NumberValue()
: 1.0;
- }()),
- map(std::make_unique<mbgl::Map>(backend,
- mbgl::Size{ 256, 256 },
+ }())
+ , 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,
+ mapObserver,
+ frontend->getSize(),
pixelRatio,
*this,
threadpool,
- mbgl::MapMode::Still)),
+ mode)),
async(new uv_async_t) {
async->data = this;
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index 7b81ecd894..b84f4d576f 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -4,8 +4,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/storage/file_source.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
+#include <mbgl/util/image.hpp>
#include <exception>
@@ -15,12 +14,15 @@
#include <nan.h>
#pragma GCC diagnostic pop
+namespace mbgl {
+class Map;
+class HeadlessFrontend;
+} // namespace mbgl
+
namespace node_mbgl {
-class NodeBackend : public mbgl::HeadlessBackend {
-public:
- NodeBackend();
- void onDidFailLoadingMap(std::exception_ptr) final;
+class NodeMapObserver : public mbgl::MapObserver {
+ void onDidFailLoadingMap(std::exception_ptr) override;
};
class NodeMap : public Nan::ObjectWrap,
@@ -43,6 +45,7 @@ public:
static void Release(const Nan::FunctionCallbackInfo<v8::Value>&);
static void Cancel(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddSource(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void RemoveSource(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
static void RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddImage(const Nan::FunctionCallbackInfo<v8::Value>&);
@@ -54,6 +57,9 @@ 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 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>&);
@@ -68,9 +74,10 @@ public:
std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback);
const float pixelRatio;
- NodeBackend backend;
- std::unique_ptr<mbgl::OffscreenView> view;
+ mbgl::MapMode mode;
NodeThreadPool threadpool;
+ NodeMapObserver mapObserver;
+ std::unique_ptr<mbgl::HeadlessFrontend> frontend;
std::unique_ptr<mbgl::Map> map;
std::exception_ptr error;
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/src/node_request.cpp b/platform/node/src/node_request.cpp
index 09373b1779..de16710f78 100644
--- a/platform/node/src/node_request.cpp
+++ b/platform/node/src/node_request.cpp
@@ -122,19 +122,9 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>& inf
}
void NodeRequest::Execute() {
- asyncExecute = std::make_unique<mbgl::util::AsyncTask>([this] { doExecute(); Unref(); });
- asyncExecute->send();
-
- Ref();
-}
-
-void NodeRequest::doExecute() {
- Nan::HandleScope scope;
-
v8::Local<v8::Value> argv[] = { handle() };
Nan::MakeCallback(Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv);
- asyncExecute.reset();
}
NodeRequest::NodeAsyncRequest::NodeAsyncRequest(NodeRequest* request_) : request(request_) {
diff --git a/platform/node/src/node_request.hpp b/platform/node/src/node_request.hpp
index 356566132b..7d7679a3c7 100644
--- a/platform/node/src/node_request.hpp
+++ b/platform/node/src/node_request.hpp
@@ -8,9 +8,6 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/file_source.hpp>
-#include <mbgl/util/async_task.hpp>
-
-#include <memory>
namespace node_mbgl {
@@ -38,12 +35,9 @@ public:
void Execute();
private:
- void doExecute();
-
NodeMap* target;
mbgl::FileSource::Callback callback;
NodeAsyncRequest* asyncRequest = nullptr;
- std::unique_ptr<mbgl::util::AsyncTask> asyncExecute;
};
}
diff --git a/platform/node/test/expression.test.js b/platform/node/test/expression.test.js
new file mode 100644
index 0000000000..aac039ce18
--- /dev/null
+++ b/platform/node/test/expression.test.js
@@ -0,0 +1,71 @@
+'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 result = {
+ compiled
+ };
+
+ const spec = fixture.propertySpec || {};
+ const expression = mbgl.Expression.parse(fixture.expression, getExpectedType(spec));
+
+ if (expression instanceof mbgl.Expression) {
+ compiled.result = 'success';
+ compiled.isFeatureConstant = expression.isFeatureConstant();
+ compiled.isZoomConstant = expression.isZoomConstant();
+ compiled.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) {
+ result.outputs = evaluateResults;
+ }
+ } else {
+ compiled.result = 'error';
+ compiled.errors = expression;
+ }
+
+ return result;
+});
+
diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json
new file mode 100644
index 0000000000..62e042cd4d
--- /dev/null
+++ b/platform/node/test/ignores.json
@@ -0,0 +1,153 @@
+{
+ "expression-tests/array/implicit-1": "https://github.com/mapbox/mapbox-gl-native/issues/10533",
+ "expression-tests/array/implicit-2": "https://github.com/mapbox/mapbox-gl-native/issues/10533",
+ "expression-tests/array/implicit-3": "https://github.com/mapbox/mapbox-gl-native/issues/10533",
+ "expression-tests/coalesce/inference": "https://github.com/mapbox/mapbox-gl-native/issues/10588",
+ "expression-tests/equal/array": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/equal/color": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/equal/mismatch": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/equal/null-lhs": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/equal/null-rhs": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/equal/number": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/equal/object": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/equal/string": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/equal/value": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/not_equal/mismatch": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/not_equal/number": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/not_equal/string": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/not_equal/value": "https://github.com/mapbox/mapbox-gl-native/issues/10678",
+ "expression-tests/interpolate/linear-color": "https://github.com/mapbox/mapbox-gl-native/issues/10604",
+ "expression-tests/to-rgba/alpha": "https://github.com/mapbox/mapbox-gl-native/issues/10604",
+ "expression-tests/to-rgba/basic": "https://github.com/mapbox/mapbox-gl-native/issues/10604",
+ "query-tests/circle-stroke-width/inside": "https://github.com/mapbox/mapbox-gl-native/issues/10307",
+ "query-tests/geometry/multilinestring": "needs investigation",
+ "query-tests/geometry/multipolygon": "needs investigation",
+ "query-tests/geometry/polygon": "needs investigation",
+ "query-tests/symbol/panned-after-insert": "https://github.com/mapbox/mapbox-gl-native/issues/10408",
+ "query-tests/symbol/rotated-after-insert": "https://github.com/mapbox/mapbox-gl-native/issues/10408",
+ "query-tests/world-wrapping/box": "skip - needs issue",
+ "query-tests/world-wrapping/point": "skip - needs issue",
+ "render-tests/background-color/transition": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
+ "render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/collision-lines": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
+ "render-tests/debug/collision-lines-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/fill-extrusion-pattern/@2x": "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/heatmap-color/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-color/expression": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-color/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-intensity/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-intensity/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-intensity/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-opacity/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-opacity/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-opacity/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/antimeridian": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-radius/pitch30": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-weight/default": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-weight/identity-property-function": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/heatmap-weight/literal": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/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/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397",
+ "render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195",
+ "render-tests/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
+ "render-tests/regressions/mapbox-gl-js#2467": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
+ "render-tests/regressions/mapbox-gl-js#2762": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
+ "render-tests/regressions/mapbox-gl-js#2769": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
+ "render-tests/regressions/mapbox-gl-js#3682": "https://github.com/mapbox/mapbox-gl-js/issues/3682",
+ "render-tests/regressions/mapbox-gl-js#4172": "https://github.com/mapbox/mapbox-gl-native/issues/10618",
+ "render-tests/regressions/mapbox-gl-js#5370": "skip - https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/regressions/mapbox-gl-js#5599": "https://github.com/mapbox/mapbox-gl-native/issues/10399",
+ "render-tests/regressions/mapbox-gl-js#5740": "https://github.com/mapbox/mapbox-gl-native/issues/10619",
+ "render-tests/regressions/mapbox-gl-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-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-font/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/10535",
+ "render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "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/viewport-text-rotation-alignment-map": "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/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601",
+ "render-tests/background-color/colorSpace-hcl": "needs issue",
+ "render-tests/hillshade-accent-color/default": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/hillshade-accent-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/hillshade-accent-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/hillshade-highlight-color/default": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/hillshade-highlight-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/hillshade-highlight-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/hillshade-shadow-color/default": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/hillshade-shadow-color/literal": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/hillshade-shadow-color/zoom-function": "skip - https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/background-opaque--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/background-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "render-tests/combinations/circle-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146",
+ "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/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/fill-opaque--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/fill-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--background-opaque": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--background-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--circle-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--fill-extrusion-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--fill-opaque": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--fill-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--line-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--raster-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/hillshade-translucent--symbol-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/line-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/raster-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/symbol-translucent--hillshade-translucent": "https://github.com/mapbox/mapbox-gl-native/pull/10642",
+ "render-tests/combinations/background-opaque--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/background-translucent--fill-extrusion-translucent": "needs investigation",
+ "render-tests/combinations/circle-translucent--fill-extrusion-translucent": "needs investigation",
+ "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--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/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 04d02d0558..e3c8031908 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -109,6 +109,7 @@ test('Map', function(t) {
'release',
'cancel',
'addSource',
+ 'removeSource',
'addLayer',
'removeLayer',
'addImage',
@@ -120,6 +121,9 @@ test('Map', function(t) {
'setZoom',
'setBearing',
'setPitch',
+ 'setAxonometric',
+ 'setXSkew',
+ 'setYSkew',
'dumpDebugLogs',
'queryRenderedFeatures'
]);
diff --git a/platform/node/test/js/request.test.js b/platform/node/test/js/request.test.js
new file mode 100644
index 0000000000..4f8d1cabb0
--- /dev/null
+++ b/platform/node/test/js/request.test.js
@@ -0,0 +1,167 @@
+'use strict';
+
+var mockfs = require('../mockfs');
+var mbgl = require('../../index');
+var test = require('tape');
+
+[ 'sprite_png', 'sprite_json', 'source_vector', 'glyph' ].forEach(function (resource) {
+ test(`render reports an error when the request function responds with an error (${resource})`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs[resource] === data) {
+ callback(new Error('message'));
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+ t.end();
+ });
+ });
+});
+
+[ 'vector', 'raster' ].forEach(function (type) {
+ test(`render does not report an error when the request function responds with no data for a tile (${type})`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs[`tile_${type}`] === data) {
+ callback();
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs[`style_${type}`]);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.error(err);
+ t.assert(pixels);
+ t.end();
+ });
+ });
+
+ test(`render reports an error when the request function responds with an error for a tile (${type})`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs[`tile_${type}`] === data) {
+ callback(new Error('message'));
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs[`style_${type}`]);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+ t.end();
+ });
+ });
+});
+
+test(`render reports an error if the request function throws an exception`, function(t) {
+ var map = new mbgl.Map({
+ request: function() {
+ throw new Error('message');
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+ t.end();
+ });
+});
+
+test(`render ignores request functions throwing an exception after calling the callback`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ callback(null, { data: data });
+ throw new Error('message');
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.error(err);
+ t.assert(pixels);
+ t.end();
+ });
+});
+
+test(`render ignores request functions calling the callback a second time`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ callback(null, { data: data });
+ callback(null, { data: data });
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.error(err);
+ t.assert(pixels);
+ t.end();
+ });
+});
+
+test(`render reports an error from loading the current style`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs.source_vector === data) {
+ callback(new Error('message'));
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+ t.end();
+ });
+ });
+});
+
+test(`render does not report an error from rendering a previous style`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs.source_vector === data) {
+ callback(new Error('message'));
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+
+ map.load(mockfs.style_raster);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.error(err);
+ t.assert(pixels);
+ t.end();
+ });
+ });
+});
diff --git a/platform/node/test/js/request_fail.test.js b/platform/node/test/js/request_fail.test.js
deleted file mode 100644
index fad116a2b8..0000000000
--- a/platform/node/test/js/request_fail.test.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict';
-
-var mockfs = require('../mockfs');
-var mbgl = require('../../index');
-var test = require('tape');
-
-function asyncReply(callback, data) {
- setTimeout(function() { callback(null, { data: data }); }, 0);
-};
-
-function asyncFail(callback) {
- setTimeout(function() { callback(new Error('not found')); }, 0);
-};
-
-function failRequest(t, style, failedResource) {
- var options = {
- request: function(req, callback) {
- var data = mockfs.dataForRequest(req);
-
- if (failedResource != data) {
- asyncReply(callback, data);
- } else {
- asyncFail(callback);
- }
- },
- ratio: 2,
- };
-
- var map = new mbgl.Map(options);
- map.load(style);
-
- map.render({ zoom: 16 }, function(err, pixels) {
- if (err) {
- t.pass("pass");
- map.release();
- }
- });
-};
-
-test('Vector', function(t) {
- t.plan(5);
-
- failRequest(t, mockfs.style_vector, null);
- failRequest(t, mockfs.style_vector, mockfs.sprite_png);
- failRequest(t, mockfs.style_vector, mockfs.sprite_json);
- failRequest(t, mockfs.style_vector, mockfs.source_vector);
- failRequest(t, mockfs.style_vector, mockfs.tile_vector);
- failRequest(t, mockfs.style_vector, mockfs.glyph);
-});
-
-test('Raster', function(t) {
- t.plan(4);
-
- failRequest(t, mockfs.style_raster, null);
- failRequest(t, mockfs.style_raster, mockfs.sprite_png);
- failRequest(t, mockfs.style_raster, mockfs.sprite_json);
- failRequest(t, mockfs.style_raster, mockfs.source_raster);
- failRequest(t, mockfs.style_raster, mockfs.tile_raster);
-});
diff --git a/platform/node/test/js/request_notfound.test.js b/platform/node/test/js/request_notfound.test.js
deleted file mode 100644
index d2d2812784..0000000000
--- a/platform/node/test/js/request_notfound.test.js
+++ /dev/null
@@ -1,74 +0,0 @@
-'use strict';
-
-var mockfs = require('../mockfs');
-var mbgl = require('../../index');
-var test = require('tape');
-
-function isTile(data) {
- return data == mockfs.tile_vector || data == mockfs.tile_raster;
-}
-
-function asyncReply(callback, data) {
- setTimeout(function() { callback(null, { data: data }); }, 0);
-};
-
-function asyncFail(callback, data) {
- // Do not set an error for tile when not found. A not found
- // tile is a valid tile.
- if (isTile(data)) {
- setTimeout(function() { callback(); }, 0);
- } else {
- setTimeout(function() { callback(new Error('not found')); }, 0);
- }
-};
-
-function notfoundRequest(t, style, notfoundResource) {
- var options = {
- request: function(req, callback) {
- var data = mockfs.dataForRequest(req);
-
- if (notfoundResource != data) {
- asyncReply(callback, data);
- } else {
- asyncFail(callback, data);
- }
- },
- ratio: 2,
- };
-
- var map = new mbgl.Map(options);
- map.load(style);
-
- map.render({ zoom: 16 }, function(err, pixels) {
- if (err && !isTile(notfoundResource)) {
- t.pass("pass");
- return;
- }
-
- if (!err && isTile(notfoundResource)) {
- t.pass("pass");
- return;
- }
-
- t.fail("fail");
- });
-};
-
-test('Vector', function(t) {
- t.plan(5);
-
- notfoundRequest(t, mockfs.style_vector, mockfs.sprite_png);
- notfoundRequest(t, mockfs.style_vector, mockfs.sprite_json);
- notfoundRequest(t, mockfs.style_vector, mockfs.source_vector);
- notfoundRequest(t, mockfs.style_vector, mockfs.tile_vector);
- notfoundRequest(t, mockfs.style_vector, mockfs.glyph);
-});
-
-test('Raster', function(t) {
- t.plan(4);
-
- notfoundRequest(t, mockfs.style_raster, mockfs.sprite_png);
- notfoundRequest(t, mockfs.style_raster, mockfs.sprite_json);
- notfoundRequest(t, mockfs.style_raster, mockfs.source_raster);
- notfoundRequest(t, mockfs.style_raster, mockfs.tile_raster);
-});
diff --git a/platform/node/test/mockfs.js b/platform/node/test/mockfs.js
index dfa5a425e3..2d27f3bbbe 100644
--- a/platform/node/test/mockfs.js
+++ b/platform/node/test/mockfs.js
@@ -5,7 +5,7 @@ var path = require('path');
function readFixture(file) {
return fs.readFileSync(path.join('test/fixtures/resources', file));
-};
+}
var style_raster = readFixture('style_raster.json').toString('utf8');
var style_vector = readFixture('style_vector.json').toString('utf8');
@@ -18,7 +18,7 @@ var tile_raster = readFixture('raster.tile');
var tile_vector = readFixture('vector.tile');
function dataForRequest(req) {
- if (req.url == null) {
+ if (req.url === null) {
return null;
} else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('json')) {
return sprite_json;
@@ -37,7 +37,7 @@ function dataForRequest(req) {
} else {
return null;
}
-};
+}
module.exports = {
dataForRequest: dataForRequest,
diff --git a/platform/node/test/query.test.js b/platform/node/test/query.test.js
index 8125f1c7cd..02602d3f5a 100644
--- a/platform/node/test/query.test.js
+++ b/platform/node/test/query.test.js
@@ -1,12 +1,13 @@
'use strict';
-var suite = require('../../../mapbox-gl-js/test/integration').query;
-var suiteImplementation = require('./suite_implementation');
+const suite = require('../../../mapbox-gl-js/test/integration').query;
+const suiteImplementation = require('./suite_implementation');
+const ignores = require('./ignores.json');
-var tests;
+let tests;
if (process.argv[1] === __filename && process.argv.length > 2) {
tests = process.argv.slice(2);
}
-suite.run('native', {tests: tests}, suiteImplementation);
+suite.run('native', {tests: tests, ignores: ignores}, suiteImplementation);
diff --git a/platform/node/test/render.test.js b/platform/node/test/render.test.js
index e2e13534c9..812a531f20 100644
--- a/platform/node/test/render.test.js
+++ b/platform/node/test/render.test.js
@@ -1,12 +1,7 @@
'use strict';
-var suite = require('../../../mapbox-gl-js/test/integration').render;
-var suiteImplementation = require('./suite_implementation');
+const suite = require('../../../mapbox-gl-js/test/integration').render;
+const suiteImplementation = require('./suite_implementation');
+const ignores = require('./ignores.json');
-var tests;
-
-if (process.argv[1] === __filename && process.argv.length > 2) {
- tests = process.argv.slice(2);
-}
-
-suite.run('native', {tests: tests}, suiteImplementation);
+suite.run('native', ignores, suiteImplementation);
diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js
index b717ecd2b2..b0be2fa0eb 100644
--- a/platform/node/test/suite_implementation.js
+++ b/platform/node/test/suite_implementation.js
@@ -10,23 +10,31 @@ mbgl.on('message', function(msg) {
console.log('%s (%s): %s', msg.severity, msg.class, msg.text);
});
+// Map of map objects by pixel ratio
+var maps = new Map();
+
module.exports = function (style, options, callback) {
- var map = new mbgl.Map({
- ratio: options.pixelRatio,
- request: function(req, callback) {
- request(req.url, {encoding: null}, function (err, response, body) {
- if (err) {
- callback(err);
- } else if (response.statusCode == 404) {
- callback();
- } else if (response.statusCode != 200) {
- callback(new Error(response.statusMessage));
- } else {
- callback(null, {data: body});
- }
- });
+ var tileMode = options.mapMode === 'tile';
+ if (options.recycleMap) {
+ var key = options.pixelRatio + '/' + tileMode;
+ if (maps.has(key)) {
+ var map = maps.get(key);
+ map.request = mapRequest;
+ } else {
+ maps.set(key, new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: mapRequest,
+ mode: options.mapMode
+ }));
+ var map = maps.get(key);
}
- });
+ } else {
+ var map = new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: mapRequest,
+ mode: options.mapMode
+ });
+ }
var timedOut = false;
var watchdog = setTimeout(function () {
@@ -46,14 +54,30 @@ module.exports = function (style, options, callback) {
options.bearing = style.bearing || 0;
options.pitch = style.pitch || 0;
- map.load(style);
+ map.load(style, { defaultStyleCamera: true });
+
+ function mapRequest(req, callback) {
+ request(req.url, {encoding: null}, function (err, response, body) {
+ if (err) {
+ callback(err);
+ } else if (response.statusCode == 404) {
+ callback();
+ } else if (response.statusCode != 200) {
+ callback(new Error(response.statusMessage));
+ } else {
+ callback(null, {data: body});
+ }
+ });
+ };
applyOperations(options.operations, function() {
map.render(options, function (err, pixels) {
var results = options.queryGeometry ?
map.queryRenderedFeatures(options.queryGeometry, options.queryOptions || {}) :
[];
- map.release();
+ if (!options.recycleMap) {
+ map.release();
+ }
if (timedOut) return;
clearTimeout(watchdog);
callback(err, pixels, results.map(prepareFeatures));
@@ -70,14 +94,24 @@ 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] : {};
- map.addImage(operation[1], img.data, {
+ const options = {
height: img.height,
width: img.width,
- pixelRatio: operation[3] || 1
- });
+ pixelRatio: testOpts.pixelRatio || 1,
+ sdf: testOpts.sdf || false
+ }
+
+ map.addImage(operation[1], img.data, options);
applyOperations(operations.slice(1), callback);
diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp
index 03ca052ec4..c4efbfa318 100644
--- a/platform/qt/app/mapwindow.cpp
+++ b/platform/qt/app/mapwindow.cpp
@@ -254,8 +254,13 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
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 geometry { { { topLeft, bottomRight } } };
- QMapbox::LineAnnotation line { { QMapbox::ShapeAnnotationGeometry::LineStringType, geometry }, 0.5f, 1.0f, Qt::red };
+ QMapbox::CoordinatesCollections lineGeometry { { { topLeft, bottomRight } } };
+ QMapbox::ShapeAnnotationGeometry annotationGeometry { QMapbox::ShapeAnnotationGeometry::LineStringType, lineGeometry };
+ QMapbox::LineAnnotation line;
+ line.geometry = annotationGeometry;
+ line.opacity = 0.5f;
+ line.width = 1.0f;
+ line.color = Qt::red;
m_lineAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::LineAnnotation>(line));
} else {
m_map->removeAnnotation(m_lineAnnotationId.toUInt());
@@ -269,8 +274,13 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
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 geometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } };
- QMapbox::FillAnnotation fill { { QMapbox::ShapeAnnotationGeometry::PolygonType, geometry }, 0.5f, Qt::green, QVariant::fromValue<QColor>(QColor(Qt::black)) };
+ QMapbox::CoordinatesCollections fillGeometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } };
+ QMapbox::ShapeAnnotationGeometry annotationGeometry { QMapbox::ShapeAnnotationGeometry::PolygonType, fillGeometry };
+ QMapbox::FillAnnotation fill;
+ fill.geometry = annotationGeometry;
+ fill.opacity = 0.5f;
+ fill.color = Qt::green;
+ fill.outlineColor = QVariant::fromValue<QColor>(QColor(Qt::black));
m_fillAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::FillAnnotation>(fill));
} else {
m_map->removeAnnotation(m_fillAnnotationId.toUInt());
@@ -283,8 +293,8 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
m_map->removeLayer("circleLayer");
m_map->removeSource("circleSource");
} else {
- QMapbox::CoordinatesCollections geometry { { { m_map->coordinate() } } };
- QMapbox::Feature feature { QMapbox::Feature::PointType, geometry, {}, {} };
+ QMapbox::CoordinatesCollections point { { { m_map->coordinate() } } };
+ QMapbox::Feature feature { QMapbox::Feature::PointType, point, {}, {} };
QVariantMap circleSource;
circleSource["type"] = "geojson";
diff --git a/platform/qt/bitrise-qt4.yml b/platform/qt/bitrise-qt4.yml
index d8c7e0788e..4a013ea8b0 100644
--- a/platform/qt/bitrise-qt4.yml
+++ b/platform/qt/bitrise-qt4.yml
@@ -1,41 +1,15 @@
----
-format_version: 1.0.0
+format_version: 1.1.0
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
+
trigger_map:
- pattern: "*"
is_pull_request_allowed: true
workflow: primary
+
workflows:
primary:
steps:
- script:
- title: Run build
+ title: Skip Workflow
inputs:
- - content: |-
- #!/bin/bash
- set -eu -o pipefail
- brew install cmake
- brew install qt
- brew link qt
- brew linkapps qt
- export BUILDTYPE=Debug
- make qt-app
- make run-qt-test
- - is_debug: 'yes'
- - slack:
- title: Post to Slack
- inputs:
- - webhook_url: "$SLACK_HOOK_URL"
- - channel: "#gl-bots"
- - from_username: 'Bitrise Qt4 macOS'
- - from_username_on_error: 'Bitrise Qt4 macOS'
- - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- passed'
- - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- failed'
- - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
+ - content: echo "This workflow is obsolete — see CircleCi."
diff --git a/platform/qt/bitrise-qt5.yml b/platform/qt/bitrise-qt5.yml
index 0ce964e43f..4a013ea8b0 100644
--- a/platform/qt/bitrise-qt5.yml
+++ b/platform/qt/bitrise-qt5.yml
@@ -1,52 +1,15 @@
----
-format_version: 1.0.0
+format_version: 1.1.0
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
+
trigger_map:
- pattern: "*"
is_pull_request_allowed: true
workflow: primary
+
workflows:
primary:
steps:
- script:
- title: Run build
+ title: Skip Workflow
inputs:
- - content: |-
- #!/bin/bash
- set -eu -o pipefail
- sudo chown -R $USER /usr/local
- brew install cmake
- 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
- export BUILDTYPE=Debug
- make qt-app
- make run-qt-test
- - is_debug: 'yes'
- - deploy-to-bitrise-io:
- title: Deploy to Bitrise.io
- inputs:
- - deploy_path: "test/fixtures"
- - notify_user_groups: none
- - is_compress: 'true'
- - slack:
- title: Post to Slack
- inputs:
- - webhook_url: "$SLACK_HOOK_URL"
- - channel: "#gl-bots"
- - from_username: 'Bitrise Qt5 macOS'
- - from_username_on_error: 'Bitrise Qt5 macOS'
- - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- passed'
- - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}>
- for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}>
- by ${GIT_CLONE_COMMIT_COMMITER_NAME}
- failed'
- - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png
- - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png
+ - content: echo "This workflow is obsolete — see CircleCi."
diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake
index 0f2bab0516..9417963767 100644
--- a/platform/qt/config.cmake
+++ b/platform/qt/config.cmake
@@ -15,16 +15,17 @@ endif()
macro(mbgl_platform_core)
target_sources(mbgl-core
- ${MBGL_QT_FILES}
+ ${MBGL_QT_CORE_FILES}
)
target_include_directories(mbgl-core
PUBLIC platform/default
+ PRIVATE platform/qt
PRIVATE platform/qt/include
)
target_link_libraries(mbgl-core
- ${MBGL_QT_LIBRARIES}
+ ${MBGL_QT_CORE_LIBRARIES}
)
if(NOT WITH_QT_DECODERS)
@@ -48,19 +49,31 @@ macro(mbgl_platform_core)
target_sources(mbgl-core PRIVATE platform/qt/src/bidi.cpp)
endif()
+ target_sources(mbgl-core PRIVATE platform/default/local_glyph_rasterizer.cpp)
+
+endmacro()
+
+
+macro(mbgl_filesource)
+ target_sources(mbgl-filesource
+ ${MBGL_QT_FILESOURCE_FILES}
+ )
+
+ target_link_libraries(mbgl-filesource
+ ${MBGL_QT_FILESOURCE_LIBRARIES}
+ )
endmacro()
+
macro(mbgl_platform_test)
target_sources(mbgl-test
- 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/default/mbgl/gl/offscreen_view.cpp
- PRIVATE platform/default/mbgl/gl/offscreen_view.hpp
- PRIVATE platform/qt/test/headless_backend_qt.cpp
PRIVATE platform/qt/test/main.cpp
- PRIVATE platform/qt/test/qmapboxgl.cpp
+ PRIVATE platform/qt/test/qmapboxgl.test.cpp
+ PRIVATE platform/qt/test/qmapboxgl.test.cpp
+ )
+
+ target_include_directories(mbgl-test
+ PRIVATE platform/qt
)
set_source_files_properties(
@@ -72,7 +85,7 @@ macro(mbgl_platform_test)
target_link_libraries(mbgl-test
PRIVATE qmapboxgl
- ${MBGL_QT_TEST_LIBRARIES}
+ PRIVATE mbgl-filesource
)
endmacro()
diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp
index 1b61d3270f..2bb5d8705c 100644
--- a/platform/qt/include/qmapbox.hpp
+++ b/platform/qt/include/qmapbox.hpp
@@ -26,6 +26,12 @@ struct Q_DECL_EXPORT Feature {
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_) {}
+
Type type;
CoordinatesCollections geometry;
QVariantMap properties;
@@ -34,11 +40,16 @@ struct Q_DECL_EXPORT Feature {
struct Q_DECL_EXPORT ShapeAnnotationGeometry {
enum Type {
- LineStringType,
+ LineStringType = 1,
PolygonType,
MultiLineStringType,
MultiPolygonType
};
+
+ /*! Class constructor. */
+ ShapeAnnotationGeometry(Type type_ = LineStringType, const CoordinatesCollections& geometry_ = CoordinatesCollections())
+ : type(type_), geometry(geometry_) {}
+
Type type;
CoordinatesCollections geometry;
};
@@ -49,16 +60,26 @@ struct Q_DECL_EXPORT SymbolAnnotation {
};
struct Q_DECL_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_) {}
+
ShapeAnnotationGeometry geometry;
- float opacity = 1.0f;
- float width = 1.0f;
- QColor color = Qt::black;
+ float opacity;
+ float width;
+ QColor color;
};
struct Q_DECL_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_) {}
+
ShapeAnnotationGeometry geometry;
- float opacity = 1.0f;
- QColor color = Qt::black;
+ float opacity;
+ QColor color;
QVariant outlineColor;
};
diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp
index e2fb283989..ebbd949921 100644
--- a/platform/qt/include/qmapboxgl.hpp
+++ b/platform/qt/include/qmapboxgl.hpp
@@ -10,6 +10,8 @@
#include <QString>
#include <QStringList>
+#include <functional>
+
class QMapboxGLPrivate;
// This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style
@@ -59,6 +61,9 @@ 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;
ConstrainMode m_constrainMode;
@@ -69,6 +74,7 @@ private:
QString m_assetPath;
QString m_accessToken;
QString m_apiBaseUrl;
+ std::function<std::string(const std::string &&)> m_resourceTransform;
};
struct Q_DECL_EXPORT QMapboxGLCameraOptions {
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/qt.cmake b/platform/qt/qt.cmake
index cee0d1080c..ae8b4bac99 100644
--- a/platform/qt/qt.cmake
+++ b/platform/qt/qt.cmake
@@ -5,30 +5,18 @@ 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)
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-set(MBGL_QT_FILES
- # File source
- PRIVATE platform/default/asset_file_source.cpp
- PRIVATE platform/default/default_file_source.cpp
- PRIVATE platform/default/local_file_source.cpp
- PRIVATE platform/default/online_file_source.cpp
-
- # Offline
- PRIVATE platform/default/mbgl/storage/offline.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.cpp
- PRIVATE platform/default/mbgl/storage/offline_database.hpp
- PRIVATE platform/default/mbgl/storage/offline_download.cpp
- PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.hpp
-
- # Misc
- PRIVATE platform/default/logging_stderr.cpp
+set(MBGL_QT_CORE_FILES
+ # Headless view
+ PRIVATE platform/default/mbgl/gl/headless_frontend.cpp
+ 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/qt/src/headless_backend_qt.cpp
# Thread pool
PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp
@@ -36,36 +24,57 @@ set(MBGL_QT_FILES
PRIVATE platform/default/mbgl/util/default_thread_pool.cpp
PRIVATE platform/default/mbgl/util/default_thread_pool.hpp
+ # Thread
+ PRIVATE platform/qt/src/thread_local.cpp
+
# Platform integration
PRIVATE platform/qt/src/async_task.cpp
PRIVATE platform/qt/src/async_task_impl.hpp
- PRIVATE platform/qt/src/http_file_source.cpp
- PRIVATE platform/qt/src/http_file_source.hpp
- PRIVATE platform/qt/src/http_request.cpp
- PRIVATE platform/qt/src/http_request.hpp
- PRIVATE platform/qt/src/image.cpp
+ 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
- PRIVATE platform/qt/src/sqlite3.cpp
PRIVATE platform/qt/src/string_stdlib.cpp
PRIVATE platform/qt/src/timer.cpp
PRIVATE platform/qt/src/timer_impl.hpp
PRIVATE platform/qt/src/utf.cpp
)
-include_directories(
- PRIVATE platform/qt/include
+set(MBGL_QT_FILESOURCE_FILES
+ # File source
+ PRIVATE platform/qt/src/http_file_source.cpp
+ PRIVATE platform/qt/src/http_file_source.hpp
+ PRIVATE platform/qt/src/http_request.cpp
+ PRIVATE platform/qt/src/http_request.hpp
+
+ # Database
+ PRIVATE platform/qt/src/sqlite3.cpp
)
# Shared library
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/default/mbgl/util/default_styles.hpp
- platform/default/mbgl/util/default_styles.cpp
+)
+
+target_include_directories(qmapboxgl
+ PUBLIC platform/qt/include
+)
+
+target_link_libraries(qmapboxgl
+ PRIVATE mbgl-core
+ PRIVATE mbgl-filesource
+ ${MBGL_QT_CORE_LIBRARIES}
+ ${MBGL_QT_FILESOURCE_LIBRARIES}
)
# C++ app
@@ -76,27 +85,47 @@ add_executable(mbgl-qt
platform/qt/resources/common.qrc
)
+target_compile_options(qmapboxgl
+ PRIVATE -std=c++03
+)
+
+target_link_libraries(mbgl-qt
+ PRIVATE qmapboxgl
+)
+
if(WITH_QT_4)
include(platform/qt/qt4.cmake)
else()
include(platform/qt/qt5.cmake)
endif()
+xcode_create_scheme(TARGET mbgl-qt)
+
# OS specific configurations
if (MASON_PLATFORM STREQUAL "osx" OR MASON_PLATFORM STREQUAL "ios")
- list(APPEND MBGL_QT_FILES
+ list(APPEND MBGL_QT_CORE_FILES
PRIVATE platform/darwin/src/nsthread.mm
)
- list(APPEND MBGL_QT_LIBRARIES
+ list(APPEND MBGL_QT_CORE_LIBRARIES
PRIVATE "-framework Foundation"
- PRIVATE "-framework OpenGL"
)
-else()
- list(APPEND MBGL_QT_FILES
+ 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_LIBRARIES
- PRIVATE -lGL
+ if(WITH_QT_4)
+ list(APPEND MBGL_QT_CORE_LIBRARIES
+ PRIVATE "-lGL"
+ )
+ endif()
+elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+ list(APPEND MBGL_QT_CORE_FILES
+ PRIVATE platform/qt/src/thread.cpp
)
endif()
@@ -107,3 +136,5 @@ add_custom_command(
${CMAKE_SOURCE_DIR}/platform/qt/include
${CMAKE_CURRENT_BINARY_DIR}/platform/qt/include
)
+
+xcode_create_scheme(TARGET qmapboxgl)
diff --git a/platform/qt/qt4.cmake b/platform/qt/qt4.cmake
index 66aed87c38..272009c541 100644
--- a/platform/qt/qt4.cmake
+++ b/platform/qt/qt4.cmake
@@ -1,31 +1,20 @@
find_package(Qt4 REQUIRED)
-set(MBGL_QT_LIBRARIES
- PRIVATE Qt4::QtCore
- PRIVATE Qt4::QtGui
- PRIVATE Qt4::QtNetwork
- PRIVATE Qt4::QtOpenGL
- PRIVATE Qt4::QtSql
+set(MBGL_QT_CORE_LIBRARIES
+ PUBLIC Qt4::QtCore
+ PUBLIC Qt4::QtGui
+ PUBLIC Qt4::QtOpenGL
)
-set(MBGL_QT_TEST_LIBRARIES
- PRIVATE Qt4::QtCore
- PRIVATE Qt4::QtOpenGL
+set(MBGL_QT_FILESOURCE_LIBRARIES
+ PUBLIC Qt4::QtNetwork
+ PUBLIC Qt4::QtSql
)
target_compile_options(qmapboxgl
PRIVATE -Wno-inconsistent-missing-override
)
-target_link_libraries(qmapboxgl
- PRIVATE mbgl-core
- PRIVATE Qt4::QtCore
- PRIVATE Qt4::QtGui
- PRIVATE Qt4::QtOpenGL
-)
-
target_link_libraries(mbgl-qt
- PRIVATE qmapboxgl
- PRIVATE Qt4::QtGui
PRIVATE Qt4::QtOpenGL
)
diff --git a/platform/qt/qt5.cmake b/platform/qt/qt5.cmake
index ed51051311..0c9a974b4b 100644
--- a/platform/qt/qt5.cmake
+++ b/platform/qt/qt5.cmake
@@ -1,35 +1,27 @@
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
-find_package(Qt5Location REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Sql REQUIRED)
-set(MBGL_QT_LIBRARIES
- PRIVATE Qt5::Core
- PRIVATE Qt5::Gui
- PRIVATE Qt5::Network
- PRIVATE Qt5::Sql
-)
+# Qt5 always build OpenGL ES2 which is the compatibility
+# mode. Qt5 will take care of translating the desktop
+# version of OpenGL to ES2.
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMBGL_USE_GLES2")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMBGL_USE_GLES2")
-set(MBGL_QT_TEST_LIBRARIES
- PRIVATE Qt5::Core
- PRIVATE Qt5::Gui
- PRIVATE Qt5::Widgets
- PRIVATE Qt5::OpenGL
+set(MBGL_QT_CORE_LIBRARIES
+ PUBLIC Qt5::Core
+ PUBLIC Qt5::Gui
+ PUBLIC Qt5::OpenGL
)
-target_link_libraries(qmapboxgl
- PRIVATE mbgl-core
- PRIVATE Qt5::Core
- PRIVATE Qt5::Gui
- PRIVATE Qt5::Location
- PRIVATE Qt5::Sql
+set(MBGL_QT_FILESOURCE_LIBRARIES
+ PUBLIC Qt5::Network
+ PUBLIC Qt5::Sql
)
target_link_libraries(mbgl-qt
- PRIVATE qmapboxgl
- PRIVATE Qt5::OpenGL
PRIVATE Qt5::Widgets
)
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/test/headless_backend_qt.cpp b/platform/qt/src/headless_backend_qt.cpp
index 5f95b2f96a..ad3fa42290 100644
--- a/platform/qt/test/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 6141216c65..ea3f388bd5 100644
--- a/platform/qt/src/http_request.cpp
+++ b/platform/qt/src/http_request.cpp
@@ -6,6 +6,7 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/util/http_header.hpp>
#include <mbgl/util/string.hpp>
+#include <mbgl/util/version.hpp>
#include <QByteArray>
#include <QNetworkReply>
@@ -37,7 +38,9 @@ QNetworkRequest HTTPRequest::networkRequest() const
{
QNetworkRequest req = QNetworkRequest(requestUrl());
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
- req.setRawHeader("User-Agent", "MapboxGL/1.0 [Qt]");
+
+ static const QByteArray agent = QString("MapboxGL/%1 (Qt %2)").arg(version::revision).arg(QT_VERSION_STR).toLatin1();
+ req.setRawHeader("User-Agent", agent);
if (m_resource.priorEtag) {
const auto etag = m_resource.priorEtag;
@@ -49,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;
@@ -79,7 +82,9 @@ void HTTPRequest::handleNetworkReply(QNetworkReply *reply)
} else if (header == "etag") {
response.etag = std::string(line.second.constData(), line.second.size());
} else if (header == "cache-control") {
- response.expires = http::CacheControl::parse(line.second.constData()).toTimePoint();
+ const auto cc = http::CacheControl::parse(line.second.constData());
+ response.expires = cc.toTimePoint();
+ response.mustRevalidate = cc.mustRevalidate;
} else if (header == "expires") {
response.expires = util::parseTimestamp(line.second.constData());
} else if (header == "retry-after") {
@@ -93,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 751b16f9db..aad32a35dc 100644
--- a/platform/qt/src/qmapbox.cpp
+++ b/platform/qt/src/qmapbox.cpp
@@ -216,7 +216,7 @@ Q_DECL_EXPORT NetworkMode networkMode()
Forwards the network status \a mode to Mapbox GL Native engine.
- File source requests uses the available network when \a mode is set to \a
+ 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)
diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp
index f094e2a0ec..2675d87862 100644
--- a/platform/qt/src/qmapboxgl.cpp
+++ b/platform/qt/src/qmapboxgl.cpp
@@ -1,5 +1,6 @@
#include "qmapboxgl.hpp"
#include "qmapboxgl_p.hpp"
+#include "qmapboxgl_renderer_frontend_p.hpp"
#include "qt_conversion.hpp"
#include "qt_geojson.hpp"
@@ -7,16 +8,28 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/math/log2.hpp>
#include <mbgl/math/minmax.hpp>
#include <mbgl/style/style.hpp>
#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>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/constants.hpp>
@@ -26,6 +39,7 @@
#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>
@@ -70,6 +84,19 @@ namespace {
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();
+
+ if (!fs) {
+ weak = fs = std::make_shared<mbgl::DefaultFileSource>(
+ cachePath, assetRoot, maximumCacheSize);
+ }
+
+ return fs;
+}
+
// Conversion helper functions.
mbgl::Size sanitizedSize(const QSize& size) {
@@ -84,8 +111,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(),
@@ -106,6 +138,11 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage
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.
+
\since 4.7
*/
@@ -118,9 +155,11 @@ 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()
*/
@@ -341,6 +380,27 @@ void QMapboxGLSettings::setApiBaseUrl(const QString& url)
}
/*!
+ Returns resource transformation callback used to transform requested URLs.
+*/
+std::function<std::string(const std::string &&)> QMapboxGLSettings::resourceTransform() const
+{
+ return m_resourceTransform;
+}
+
+/*!
+ Sets the resource \a transform callback.
+
+ When given, resource transformation callback will be used to transform the
+ requested resource URLs before they are requested from internet. This can be
+ used add or remove custom parameters, or reroute certain requests to other
+ servers or endpoints.
+*/
+void QMapboxGLSettings::setResourceTransform(const std::function<std::string(const std::string &&)> &transform)
+{
+ m_resourceTransform = transform;
+}
+
+/*!
\class QMapboxGL
\brief The QMapboxGL class is a Qt wrapper for the Mapbox GL Native engine.
@@ -426,13 +486,13 @@ void QMapboxGLSettings::setApiBaseUrl(const QString& url)
*/
/*!
- Constructs a QMapboxGL object with \a settings and sets \a parent as the parent
+ Constructs a QMapboxGL object with \a settings and sets \a parent_ as the parent
object. The \a settings cannot be changed after the object is constructed. The
\a size represents the size of the viewport and the \a pixelRatio the initial pixel
density of the screen.
*/
-QMapboxGL::QMapboxGL(QObject *parent, const QMapboxGLSettings &settings, const QSize& size, qreal pixelRatio)
- : QObject(parent)
+QMapboxGL::QMapboxGL(QObject *parent_, const QMapboxGLSettings &settings, const QSize& size, qreal pixelRatio)
+ : QObject(parent_)
{
assert(!size.isEmpty());
@@ -563,7 +623,7 @@ double QMapboxGL::scale() const
void QMapboxGL::setScale(double scale_, const QPointF &center)
{
- d_ptr->mapObj->setZoom(std::log2(scale_), mbgl::ScreenCoordinate { center.x(), center.y() });
+ d_ptr->mapObj->setZoom(mbgl::util::log2(scale_), mbgl::ScreenCoordinate { center.x(), center.y() });
}
/*!
@@ -755,21 +815,21 @@ void QMapboxGL::setTransitionOptions(qint64 duration, qint64 delay) {
d_ptr->mapObj->getStyle().setTransitionOptions(mbgl::style::TransitionOptions{ convert(duration), convert(delay) });
}
-mbgl::Annotation asMapboxGLAnnotation(const QMapbox::Annotation & annotation) {
+mbgl::optional<mbgl::Annotation> asMapboxGLAnnotation(const QMapbox::Annotation & annotation) {
auto asMapboxGLGeometry = [](const QMapbox::ShapeAnnotationGeometry &geometry) {
mbgl::ShapeAnnotationGeometry result;
switch (geometry.type) {
case QMapbox::ShapeAnnotationGeometry::LineStringType:
- result = { asMapboxGLLineString(geometry.geometry.first().first()) };
+ result = asMapboxGLLineString(geometry.geometry.first().first());
break;
case QMapbox::ShapeAnnotationGeometry::PolygonType:
- result = { asMapboxGLPolygon(geometry.geometry.first()) };
+ result = asMapboxGLPolygon(geometry.geometry.first());
break;
case QMapbox::ShapeAnnotationGeometry::MultiLineStringType:
- result = { asMapboxGLMultiLineString(geometry.geometry.first()) };
+ result = asMapboxGLMultiLineString(geometry.geometry.first());
break;
case QMapbox::ShapeAnnotationGeometry::MultiPolygonType:
- result = { asMapboxGLMultiPolygon(geometry.geometry) };
+ result = asMapboxGLMultiPolygon(geometry.geometry);
break;
}
return result;
@@ -778,19 +838,19 @@ mbgl::Annotation asMapboxGLAnnotation(const QMapbox::Annotation & annotation) {
if (annotation.canConvert<QMapbox::SymbolAnnotation>()) {
QMapbox::SymbolAnnotation symbolAnnotation = annotation.value<QMapbox::SymbolAnnotation>();
QMapbox::Coordinate& pair = symbolAnnotation.geometry;
- return mbgl::SymbolAnnotation { mbgl::Point<double> { pair.second, pair.first }, symbolAnnotation.icon.toStdString() };
+ return { mbgl::SymbolAnnotation(mbgl::Point<double> { pair.second, pair.first }, symbolAnnotation.icon.toStdString()) };
} else if (annotation.canConvert<QMapbox::LineAnnotation>()) {
QMapbox::LineAnnotation lineAnnotation = annotation.value<QMapbox::LineAnnotation>();
auto color = mbgl::Color::parse(lineAnnotation.color.name().toStdString());
- return mbgl::LineAnnotation { asMapboxGLGeometry(lineAnnotation.geometry), lineAnnotation.opacity, lineAnnotation.width, { *color } };
+ return { mbgl::LineAnnotation(asMapboxGLGeometry(lineAnnotation.geometry), lineAnnotation.opacity, lineAnnotation.width, { *color }) };
} else if (annotation.canConvert<QMapbox::FillAnnotation>()) {
QMapbox::FillAnnotation fillAnnotation = annotation.value<QMapbox::FillAnnotation>();
auto color = mbgl::Color::parse(fillAnnotation.color.name().toStdString());
if (fillAnnotation.outlineColor.canConvert<QColor>()) {
auto outlineColor = mbgl::Color::parse(fillAnnotation.outlineColor.value<QColor>().name().toStdString());
- return mbgl::FillAnnotation { asMapboxGLGeometry(fillAnnotation.geometry), fillAnnotation.opacity, { *color }, { *outlineColor } };
+ return { mbgl::FillAnnotation(asMapboxGLGeometry(fillAnnotation.geometry), fillAnnotation.opacity, { *color }, { *outlineColor }) };
} else {
- return mbgl::FillAnnotation { asMapboxGLGeometry(fillAnnotation.geometry), fillAnnotation.opacity, { *color }, {} };
+ return { mbgl::FillAnnotation(asMapboxGLGeometry(fillAnnotation.geometry), fillAnnotation.opacity, { *color }, {}) };
}
}
@@ -807,7 +867,7 @@ mbgl::Annotation asMapboxGLAnnotation(const QMapbox::Annotation & annotation) {
*/
QMapbox::AnnotationID QMapboxGL::addAnnotation(const QMapbox::Annotation &annotation)
{
- return d_ptr->mapObj->addAnnotation(asMapboxGLAnnotation(annotation));
+ return d_ptr->mapObj->addAnnotation(*asMapboxGLAnnotation(annotation));
}
/*!
@@ -817,7 +877,7 @@ QMapbox::AnnotationID QMapboxGL::addAnnotation(const QMapbox::Annotation &annota
*/
void QMapboxGL::updateAnnotation(QMapbox::AnnotationID id, const QMapbox::Annotation &annotation)
{
- d_ptr->mapObj->updateAnnotation(id, asMapboxGLAnnotation(annotation));
+ d_ptr->mapObj->updateAnnotation(id, *asMapboxGLAnnotation(annotation));
}
/*!
@@ -829,7 +889,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.
@@ -863,7 +923,7 @@ void QMapboxGL::removeAnnotation(QMapbox::AnnotationID id)
\li QVariantList
\endtable
*/
-void QMapboxGL::setLayoutProperty(const QString& layer, const QString& property, const QVariant& value)
+void QMapboxGL::setLayoutProperty(const QString& layer, const QString& property_, const QVariant& value)
{
using namespace mbgl::style;
@@ -873,14 +933,14 @@ void QMapboxGL::setLayoutProperty(const QString& layer, const QString& property,
return;
}
- if (conversion::setLayoutProperty(*layer_, property.toStdString(), value)) {
- qWarning() << "Error setting layout property:" << layer << "-" << property;
+ if (conversion::setLayoutProperty(*layer_, property_.toStdString(), value)) {
+ qWarning() << "Error setting layout property:" << layer << "-" << property_;
return;
}
}
/*!
- 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.
@@ -929,7 +989,7 @@ void QMapboxGL::setLayoutProperty(const QString& layer, const QString& property,
map->setPaintProperty("route","line-dasharray", lineDashArray);
\endcode
*/
-void QMapboxGL::setPaintProperty(const QString& layer, const QString& property, const QVariant& value)
+void QMapboxGL::setPaintProperty(const QString& layer, const QString& property_, const QVariant& value)
{
using namespace mbgl::style;
@@ -939,8 +999,8 @@ void QMapboxGL::setPaintProperty(const QString& layer, const QString& property,
return;
}
- if (conversion::setPaintProperty(*layer_, property.toStdString(), value)) {
- qWarning() << "Error setting paint property:" << layer << "-" << property;
+ if (conversion::setPaintProperty(*layer_, property_.toStdString(), value)) {
+ qWarning() << "Error setting paint property:" << layer << "-" << property_;
return;
}
}
@@ -971,7 +1031,7 @@ void QMapboxGL::moveBy(const QPointF &offset)
can be used for implementing a pinch gesture.
*/
void QMapboxGL::scaleBy(double scale_, const QPointF &center) {
- d_ptr->mapObj->setZoom(d_ptr->mapObj->getZoom() + std::log2(scale_), mbgl::ScreenCoordinate { center.x(), center.y() });
+ d_ptr->mapObj->setZoom(d_ptr->mapObj->getZoom() + mbgl::util::log2(scale_), mbgl::ScreenCoordinate { center.x(), center.y() });
}
/*!
@@ -1009,7 +1069,7 @@ void QMapboxGL::resize(const QSize& size, const QSize& framebufferSize)
}
/*!
- If Mapbox GL needs to rebind the default framebuffer, it will use the
+ If Mapbox GL needs to rebind the default \a fbo, it will use the
ID supplied here.
*/
void QMapboxGL::setFramebufferObject(quint32 fbo) {
@@ -1033,11 +1093,11 @@ void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &icon)
}
/*!
- Returns the amount of meters per pixel from a given \a latitude and \a zoom.
+ Returns the amount of meters per pixel from a given \a latitude_ and \a zoom_.
*/
-double QMapboxGL::metersPerPixelAtLatitude(double latitude, double zoom) const
+double QMapboxGL::metersPerPixelAtLatitude(double latitude_, double zoom_) const
{
- return mbgl::Projection::getMetersPerPixelAtLatitude(latitude, zoom);
+ return mbgl::Projection::getMetersPerPixelAtLatitude(latitude_, zoom_);
}
/*!
@@ -1408,10 +1468,10 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter)
}
/*!
- Renders the map using OpenGL draw calls. If \a fbo is passed, 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 the canvas.
+ 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
+ the canvas.
This function should be called only after the signal needsRendering() is
emitted at least once.
@@ -1426,11 +1486,8 @@ void QMapboxGL::render()
}
#endif
- // The OpenGL implementation automatically enables the OpenGL context for us.
- mbgl::BackendScope scope { *d_ptr, mbgl::BackendScope::ScopeType::Implicit };
-
d_ptr->dirty = false;
- d_ptr->mapObj->render(*d_ptr);
+ d_ptr->render();
}
/*!
@@ -1469,21 +1526,42 @@ 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)
: QObject(q)
, size(size_)
, q_ptr(q)
- , fileSourceObj(std::make_unique<mbgl::DefaultFileSource>(
+ , fileSourceObj(sharedDefaultFileSource(
settings.cacheDatabasePath().toStdString(),
settings.assetPath().toStdString(),
settings.cacheDatabaseMaximumSize()))
, threadPool(mbgl::sharedThreadPool())
{
+ // Setup resource transform if needed
+ 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_));
+ }
+ );
+ fileSourceObj->setResourceTransform(m_resourceTransform->self());
+ }
+
+ // 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()));
+
mapObj = std::make_unique<mbgl::Map>(
+ *frontend,
*this, sanitizedSize(size),
pixelRatio, *fileSourceObj, *threadPool,
mbgl::MapMode::Continuous,
- static_cast<mbgl::GLContextMode>(settings.contextMode()),
static_cast<mbgl::ConstrainMode>(settings.constrainMode()),
static_cast<mbgl::ViewportMode>(settings.viewportMode()));
@@ -1501,18 +1579,20 @@ QMapboxGLPrivate::~QMapboxGLPrivate()
{
}
-mbgl::Size QMapboxGLPrivate::framebufferSize() const {
+mbgl::Size QMapboxGLPrivate::getFramebufferSize() const {
return sanitizedSize(fbSize);
}
void QMapboxGLPrivate::updateAssumedState() {
assumeFramebufferBinding(fbObject);
- assumeViewport(0, 0, framebufferSize());
+#if QT_VERSION >= 0x050600
+ assumeViewport(0, 0, getFramebufferSize());
+#endif
}
void QMapboxGLPrivate::bind() {
setFramebufferBinding(fbObject);
- setViewport(0, 0, framebufferSize());
+ setViewport(0, 0, getFramebufferSize());
}
void QMapboxGLPrivate::invalidate()
@@ -1523,6 +1603,11 @@ void QMapboxGLPrivate::invalidate()
}
}
+void QMapboxGLPrivate::render()
+{
+ frontend->render();
+}
+
void QMapboxGLPrivate::onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode)
{
if (mode == mbgl::MapObserver::CameraChangeMode::Immediate) {
@@ -1610,7 +1695,7 @@ void QMapboxGLPrivate::onSourceChanged(mbgl::style::Source&)
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) {
+mbgl::gl::ProcAddress QMapboxGLPrivate::getExtensionFunctionPointer(const char* name) {
#if QT_VERSION >= 0x050000
QOpenGLContext* thisContext = QOpenGLContext::currentContext();
return thisContext->getProcAddress(name);
diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp
index 49a7942cce..f947c09f48 100644
--- a/platform/qt/src/qmapboxgl_p.hpp
+++ b/platform/qt/src/qmapboxgl_p.hpp
@@ -1,18 +1,22 @@
#pragma once
#include "qmapboxgl.hpp"
+#include "qmapboxgl_renderer_frontend_p.hpp"
+#include <mbgl/actor/actor.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend.hpp>
-#include <mbgl/map/view.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/geo.hpp>
+#include <mbgl/storage/resource_transform.hpp>
#include <QObject>
#include <QSize>
-class QMapboxGLPrivate : public QObject, public mbgl::View, public mbgl::Backend
+#include <memory>
+
+class QMapboxGLPrivate : public QObject, public mbgl::RendererBackend, public mbgl::MapObserver
{
Q_OBJECT
@@ -20,14 +24,11 @@ public:
explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio);
virtual ~QMapboxGLPrivate();
- mbgl::Size framebufferSize() const;
- // mbgl::View implementation.
+ // mbgl::RendererBackend implementation.
void bind() final;
-
- // mbgl::Backend implementation.
+ mbgl::Size getFramebufferSize() const final;
void updateAssumedState() final;
- void invalidate() final;
void activate() final {}
void deactivate() final {}
@@ -52,20 +53,26 @@ public:
QMapboxGL *q_ptr { nullptr };
- std::unique_ptr<mbgl::DefaultFileSource> fileSourceObj;
+ 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;
+ mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) override;
public slots:
void connectionEstablished();
+ void invalidate();
+ void render();
signals:
void needsRendering();
void mapChanged(QMapboxGL::MapChange);
void copyrightsChanged(const QString &copyrightsHtml);
+
+private:
+ std::unique_ptr< mbgl::Actor<mbgl::ResourceTransform> > m_resourceTransform;
};
diff --git a/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp b/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp
new file mode 100644
index 0000000000..ea60851eb4
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_renderer_frontend_p.cpp
@@ -0,0 +1,37 @@
+#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
new file mode 100644
index 0000000000..c5e2bacc34
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_renderer_frontend_p.hpp
@@ -0,0 +1,35 @@
+#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/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/image.cpp b/platform/qt/src/qt_image.cpp
index 403ca9cbd3..a5c92514c1 100644
--- a/platform/qt/src/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 100755
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 c44f284852..af0c50ebb9 100644
--- a/platform/qt/src/run_loop.cpp
+++ b/platform/qt/src/run_loop.cpp
@@ -1,6 +1,6 @@
#include "run_loop_impl.hpp"
-#include <mbgl/util/thread_local.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <QCoreApplication>
@@ -8,13 +8,6 @@
#include <functional>
#include <utility>
-namespace {
-
-using namespace mbgl::util;
-static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
-
-}
-
namespace mbgl {
namespace util {
@@ -27,7 +20,8 @@ void RunLoop::Impl::onWriteEvent(int fd) {
}
RunLoop* RunLoop::Get() {
- return current.get();
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
}
RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) {
@@ -42,14 +36,14 @@ RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) {
impl->type = type;
- current.set(this);
+ Scheduler::SetCurrent(this);
impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
}
RunLoop::~RunLoop() {
MBGL_VERIFY_THREAD(tid);
- current.set(nullptr);
+ Scheduler::SetCurrent(nullptr);
}
LOOP_HANDLE RunLoop::getLoopHandle() {
@@ -59,8 +53,10 @@ LOOP_HANDLE RunLoop::getLoopHandle() {
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
- withMutex([&] { queue.push(task); });
- impl->async->send();
+ withMutex([&] {
+ queue.push(std::move(task));
+ impl->async->send();
+ });
}
void RunLoop::run() {
diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp
index 8df279c25d..2f3db12f33 100644
--- a/platform/qt/src/sqlite3.cpp
+++ b/platform/qt/src/sqlite3.cpp
@@ -6,11 +6,13 @@
#include <QStringList>
#include <QThread>
#include <QVariant>
+#include <QAtomicInt>
#include <cassert>
#include <cstring>
#include <cstdio>
#include <chrono>
+#include <limits>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/logging.hpp>
@@ -50,20 +52,35 @@ void checkDatabaseError(const QSqlDatabase &db) {
}
}
+void checkDatabaseOpenError(const QSqlDatabase &db) {
+ // Assume every error when opening the data as CANTOPEN. Qt
+ // 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." };
+ }
+}
+
+namespace {
+ QString incrementCounter() {
+ static QAtomicInt count = 0;
+ return QString::number(count.fetchAndAddAcquire(1));
+ }
+}
+
class DatabaseImpl {
public:
- DatabaseImpl(const char* filename, int flags) {
- static uint64_t count = 0;
- const QString connectionName = QString::number(uint64_t(QThread::currentThread())) + QString::number(count++);
-
+ DatabaseImpl(const char* filename, int flags)
+ : connectionName(QString::number(uint64_t(QThread::currentThread())) + incrementCounter())
+ {
if (!QSqlDatabase::drivers().contains("QSQLITE")) {
throw Exception { Exception::Code::CANTOPEN, "SQLite driver not found." };
}
assert(!QSqlDatabase::contains(connectionName));
- db.reset(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", connectionName)));
+ auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
- QString connectOptions = db->connectOptions();
+ QString connectOptions = db.connectOptions();
if (flags & OpenFlag::ReadOnly) {
if (!connectOptions.isEmpty()) connectOptions.append(';');
connectOptions.append("QSQLITE_OPEN_READONLY");
@@ -73,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()) {
- checkDatabaseError(*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);
}
@@ -132,18 +149,23 @@ Database::~Database() {
void Database::setBusyTimeout(std::chrono::milliseconds timeout) {
assert(impl);
- std::string timeoutStr = mbgl::util::toString(timeout.count());
- QString connectOptions = impl->db->connectOptions();
+
+ // std::chrono::milliseconds.count() is a long and Qt will cast
+ // internally to int, so we need to make sure the limits apply.
+ std::string timeoutStr = mbgl::util::toString(timeout.count() & INT_MAX);
+
+ 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()) {
- checkDatabaseError(*impl->db);
+ db.setConnectOptions(connectOptions);
+ if (!db.open()) {
+ checkDatabaseOpenError(db);
}
}
@@ -155,9 +177,9 @@ void Database::exec(const std::string &sql) {
if (!statement.endsWith(';')) {
statement.append(';');
}
- QSqlQuery query(*impl->db);
- query.setForwardOnly(true);
+ QSqlQuery query(QSqlDatabase::database(impl->connectionName));
query.prepare(statement);
+
if (!query.exec()) {
checkQueryError(query);
}
@@ -169,7 +191,7 @@ Statement Database::prepare(const char *query) {
}
Statement::Statement(Database *db, const char *sql)
- : impl(std::make_unique<StatementImpl>(QString(sql), *db->impl->db)) {
+ : impl(std::make_unique<StatementImpl>(QString(sql), QSqlDatabase::database(db->impl->connectionName))) {
assert(impl);
}
@@ -255,9 +277,13 @@ void Statement::bind(int offset, const char* value, std::size_t length, bool ret
throw std::range_error("value too long");
}
+ // Qt SQLite driver treats QByteArray as blob: we need to explicitly
+ // declare the variant type as string.
+ QVariant text(QVariant::Type::String);
+ text.setValue(retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length));
+
// Field numbering starts at 0.
- impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) :
- QByteArray::fromRawData(value, length), QSql::In);
+ impl->query.bindValue(offset - 1, std::move(text), QSql::In);
checkQueryError(impl->query);
}
@@ -267,7 +293,12 @@ void Statement::bind(int offset, const std::string& value, bool retain) {
}
void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) {
+ assert(impl);
const char* value = reinterpret_cast<const char*>(value_);
+ if (length > std::numeric_limits<int>::max()) {
+ // Kept for consistence with the default implementation.
+ throw std::range_error("value too long");
+ }
// Field numbering starts at 0.
impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) :
@@ -282,22 +313,23 @@ void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool ret
bool Statement::run() {
assert(impl);
- if (impl->query.isValid()) {
- return impl->query.next();
- }
- assert(!impl->query.isActive());
- impl->query.setForwardOnly(true);
- if (!impl->query.exec()) {
- checkQueryError(impl->query);
+ if (!impl->query.isValid()) {
+ if (impl->query.exec()) {
+ impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>();
+ impl->changes = impl->query.numRowsAffected();
+ } else {
+ checkQueryError(impl->query);
+ }
}
- impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>();
- impl->changes = impl->query.numRowsAffected();
+ const bool hasNext = impl->query.next();
+ if (!hasNext) impl->query.finish();
- return impl->query.next();
+ return hasNext;
}
+template bool Statement::get(int);
template int Statement::get(int);
template int64_t Statement::get(int);
template double Statement::get(int);
diff --git a/platform/qt/src/thread.cpp b/platform/qt/src/thread.cpp
new file mode 100644
index 0000000000..ade3629b63
--- /dev/null
+++ b/platform/qt/src/thread.cpp
@@ -0,0 +1,19 @@
+#include <mbgl/util/platform.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace platform {
+
+std::string getCurrentThreadName() {
+ return "unknown";
+}
+
+void setCurrentThreadName(const std::string&) {
+}
+
+void makeThreadLowPriority() {
+}
+
+} // namespace platform
+} // namespace mbgl
diff --git a/platform/qt/src/thread_local.cpp b/platform/qt/src/thread_local.cpp
new file mode 100644
index 0000000000..467bfb0d05
--- /dev/null
+++ b/platform/qt/src/thread_local.cpp
@@ -0,0 +1,49 @@
+#include <mbgl/util/thread_local.hpp>
+
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+
+#include <array>
+#include <cassert>
+
+#include <QThreadStorage>
+
+namespace mbgl {
+namespace util {
+
+template <class T>
+class ThreadLocal<T>::Impl {
+public:
+ QThreadStorage<std::array<T*, 1>> local;
+};
+
+template <class T>
+ThreadLocal<T>::ThreadLocal() : impl(std::make_unique<Impl>()) {
+ set(nullptr);
+}
+
+template <class T>
+ThreadLocal<T>::~ThreadLocal() {
+ // ThreadLocal will not take ownership
+ // of the pointer it is managing. The pointer
+ // needs to be explicitly cleared before we
+ // destroy this object.
+ assert(!get());
+}
+
+template <class T>
+T* ThreadLocal<T>::get() {
+ return impl->local.localData()[0];
+}
+
+template <class T>
+void ThreadLocal<T>::set(T* ptr) {
+ impl->local.localData()[0] = ptr;
+}
+
+template class ThreadLocal<Scheduler>;
+template class ThreadLocal<BackendScope>;
+template class ThreadLocal<int>; // For unit tests
+
+} // namespace util
+} // namespace mbgl
diff --git a/platform/qt/test/qmapboxgl.cpp b/platform/qt/test/qmapboxgl.cpp
deleted file mode 100644
index 453604076e..0000000000
--- a/platform/qt/test/qmapboxgl.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#include <mbgl/test/util.hpp>
-#include <mbgl/util/io.hpp>
-
-#include <QApplication>
-#include <QMapbox>
-#include <QMapboxGL>
-
-// We're using QGLFramebufferObject, which is only available in Qt 5 and up.
-#if QT_VERSION >= 0x050000
-
-#include <QGLWidget>
-#include <QGLFramebufferObject>
-
-class QMapboxGLTest : public QObject, public ::testing::Test {
- Q_OBJECT
-
-public:
- QMapboxGLTest() : fbo((assert(widget.context()->isValid()), widget.makeCurrent(), QSize(512, 512))), map(nullptr, settings) {
- 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.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14);
- }
-
- void runUntil(QMapboxGL::MapChange status) {
- changeCallback = [&](QMapboxGL::MapChange change) {
- if (change == status) {
- qApp->exit();
- changeCallback = nullptr;
- }
- };
-
- qApp->exec();
- }
-
-private:
- QGLWidget widget;
- QGLFramebufferObject fbo;
-
-protected:
- QMapboxGLSettings settings;
- QMapboxGL map;
-
- std::function<void(QMapboxGL::MapChange)> changeCallback;
-
-private slots:
- void onMapChanged(QMapboxGL::MapChange change) {
- if (changeCallback) {
- changeCallback(change);
- }
- };
-
- void onNeedsRendering() {
- widget.makeCurrent();
- fbo.bind();
- glViewport(0, 0, fbo.width(), fbo.height());
- map.render();
- };
-};
-
-TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleJson)) {
- QString json = QString::fromStdString(
- mbgl::util::read_file("test/fixtures/resources/style_vector.json"));
-
- map.setStyleJson(json);
- ASSERT_EQ(map.styleJson(), json);
- runUntil(QMapboxGL::MapChangeDidFinishLoadingMap);
-
- map.setStyleJson("invalid json");
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-
- map.setStyleJson("\"\"");
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-
- map.setStyleJson(QString());
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-}
-
-TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleUrl)) {
- QString url(QMapbox::defaultStyles()[0].first);
-
- map.setStyleUrl(url);
- ASSERT_EQ(map.styleUrl(), url);
- runUntil(QMapboxGL::MapChangeDidFinishLoadingMap);
-
- map.setStyleUrl("invalid://url");
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-
- map.setStyleUrl(QString());
- runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
-}
-
-#include "qmapboxgl.moc"
-
-#endif
diff --git a/platform/qt/test/qmapboxgl.test.cpp b/platform/qt/test/qmapboxgl.test.cpp
new file mode 100644
index 0000000000..932460b932
--- /dev/null
+++ b/platform/qt/test/qmapboxgl.test.cpp
@@ -0,0 +1,80 @@
+#include "qmapboxgl.test.hpp"
+
+#include <mbgl/util/io.hpp>
+
+#include <QMapbox>
+
+// 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.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14);
+}
+
+void QMapboxGLTest::runUntil(QMapboxGL::MapChange status) {
+ changeCallback = [&](QMapboxGL::MapChange change) {
+ if (change == status) {
+ qApp->exit();
+ changeCallback = nullptr;
+ }
+ };
+
+ qApp->exec();
+}
+
+void QMapboxGLTest::onMapChanged(QMapboxGL::MapChange change) {
+ if (changeCallback) {
+ changeCallback(change);
+ }
+}
+
+void QMapboxGLTest::onNeedsRendering() {
+ widget.makeCurrent();
+ fbo.bind();
+ QOpenGLContext::currentContext()->functions()->glViewport(0, 0, fbo.width(), fbo.height());
+ map.render();
+}
+
+
+TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleJson)) {
+ QString json = QString::fromStdString(
+ mbgl::util::read_file("test/fixtures/resources/style_vector.json"));
+
+ map.setStyleJson(json);
+ ASSERT_EQ(map.styleJson(), json);
+ runUntil(QMapboxGL::MapChangeDidFinishLoadingMap);
+
+ map.setStyleJson("invalid json");
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+
+ map.setStyleJson("\"\"");
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+
+ map.setStyleJson(QString());
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+}
+
+TEST_F(QMapboxGLTest, TEST_DISABLED_ON_CI(styleUrl)) {
+ QString url(QMapbox::defaultStyles()[0].first);
+
+ map.setStyleUrl(url);
+ ASSERT_EQ(map.styleUrl(), url);
+ runUntil(QMapboxGL::MapChangeDidFinishLoadingMap);
+
+ map.setStyleUrl("invalid://url");
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+
+ map.setStyleUrl(QString());
+ runUntil(QMapboxGL::MapChangeDidFailLoadingMap);
+}
+
+#endif
diff --git a/platform/qt/test/qmapboxgl.test.hpp b/platform/qt/test/qmapboxgl.test.hpp
new file mode 100644
index 0000000000..04d63610ca
--- /dev/null
+++ b/platform/qt/test/qmapboxgl.test.hpp
@@ -0,0 +1,36 @@
+#include <mbgl/test/util.hpp>
+
+#include <QApplication>
+#include <QMapboxGL>
+
+// We're using QGLFramebufferObject, which is only available in Qt 5 and up.
+#if QT_VERSION >= 0x050000
+
+#include <QGLWidget>
+#include <QGLFramebufferObject>
+
+class QMapboxGLTest : public QObject, public ::testing::Test {
+ Q_OBJECT
+
+public:
+ QMapboxGLTest();
+
+ void runUntil(QMapboxGL::MapChange);
+
+private:
+ QGLWidget widget;
+ const QSize size;
+ QGLFramebufferObject fbo;
+
+protected:
+ QMapboxGLSettings settings;
+ QMapboxGL map;
+
+ std::function<void(QMapboxGL::MapChange)> changeCallback;
+
+private slots:
+ void onMapChanged(QMapboxGL::MapChange);
+ void onNeedsRendering();
+};
+
+#endif
diff --git a/scripts/check-cxx11abi.sh b/scripts/check-cxx11abi.sh
index c543e52cb6..c6fd9258c5 100755
--- a/scripts/check-cxx11abi.sh
+++ b/scripts/check-cxx11abi.sh
@@ -3,7 +3,9 @@
set -e
set -o pipefail
-if [ ! `uname -s` = 'Linux' ]; then
+if ! [ `uname -s` = 'Linux' ] || \
+ ! command -v readelf > /dev/null || \
+ ! command -v c++filt > /dev/null; then
echo -n "OFF"
exit 0
fi
diff --git a/scripts/circle_setup.sh b/scripts/circle_setup.sh
deleted file mode 100755
index 308cac34fb..0000000000
--- a/scripts/circle_setup.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env bash
-# This script is sourced; do not set -e or -o pipefail here.
-
-# Touch package.json so that we are definitely going to run an npm update action
-touch package.json
-
-function mapbox_install_logbt {
- export PATH=$(scripts/mason.sh PREFIX gdb VERSION 7.12)/bin:${PATH}
- curl -sSfL https://github.com/mapbox/logbt/archive/v2.0.1.tar.gz | tar --gunzip --extract --strip-components=2 --exclude="*md" --exclude="test*" --directory=.
- ./logbt --test
-}
-
-export -f mapbox_install_logbt
-
-function mapbox_install_apitrace {
- export PATH=$(scripts/mason.sh PREFIX apitrace VERSION 6a30de1)/bin:${PATH}
-}
-
-export -f mapbox_install_apitrace
-
-function mapbox_export_mesa_library_path {
- # Install and set up to load a more recent version of mesa
- MESA_PREFIX=$(scripts/mason.sh PREFIX mesa VERSION 13.0.4)
- export LD_LIBRARY_PATH="${MESA_PREFIX}/lib:${LD_LIBRARY_PATH:-}"
- export LIBGL_DRIVERS_PATH="${MESA_PREFIX}/lib/dri"
-}
-
-export -f mapbox_export_mesa_library_path
-
-# Install and set up to load awscli
-pip install --user awscli
-export PATH="`python -m site --user-base`/bin:${PATH}"
-
-# Install coveralls gem
-gem install coveralls-lcov --no-rdoc --no-ri
diff --git a/scripts/clang-tools.sh b/scripts/clang-tools.sh
index 48675e3ab1..bdda4544b9 100755
--- a/scripts/clang-tools.sh
+++ b/scripts/clang-tools.sh
@@ -3,11 +3,11 @@
set -e
set -o pipefail
-CLANG_TIDY_PREFIX=${CLANG_TIDY_PREFIX:-$(scripts/mason.sh PREFIX clang-tidy VERSION 4.0.0)}
+CLANG_TIDY_PREFIX=${CLANG_TIDY_PREFIX:-$(scripts/mason.sh PREFIX clang-tidy VERSION 4.0.1)}
CLANG_TIDY=${CLANG_TIDY:-${CLANG_TIDY_PREFIX}/bin/clang-tidy}
CLANG_APPLY=${CLANG_APPLY:-${CLANG_TIDY_PREFIX}/bin/clang-apply-replacements}
-CLANG_FORMAT=${CLANG_FORMAT:-$(scripts/mason.sh PREFIX clang-format VERSION 4.0.0)/bin/clang-format}
+CLANG_FORMAT=${CLANG_FORMAT:-$(scripts/mason.sh PREFIX clang-format VERSION 4.0.1)/bin/clang-format}
for CLANG_FILE in "${CLANG_TIDY} ${CLANG_APPLY} ${CLANG_FORMAT}"; do
command -v ${CLANG_TIDY} > /dev/null 2>&1 || {
diff --git a/scripts/config.xcconfig.in b/scripts/config.xcconfig.in
index b70ff9d677..357732c9ae 100644
--- a/scripts/config.xcconfig.in
+++ b/scripts/config.xcconfig.in
@@ -3,4 +3,7 @@
// mbgl-core
mbgl_core_INCLUDE_DIRECTORIES = "@mbgl_core_INCLUDE_DIRECTORIES@"
mbgl_core_LINK_LIBRARIES = "@mbgl_core_LINK_LIBRARIES@"
-polylabel_INCLUDE_DIRECTORIES = "@MASON_PACKAGE_polylabel_INCLUDE_DIRS@"
+
+// mbgl-filesource
+mbgl_filesource_INCLUDE_DIRECTORIES = "@mbgl_filesource_INCLUDE_DIRECTORIES@"
+mbgl_filesource_LINK_LIBRARIES = "@mbgl_filesource_LINK_LIBRARIES@"
diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js
index a10e505278..cf54b1b100 100755
--- a/scripts/generate-shaders.js
+++ b/scripts/generate-shaders.js
@@ -1,16 +1,13 @@
#!/usr/bin/env node
-const path = require('path');
-const fs = require('fs');
+require('flow-remove-types/register');
-const inputPath = 'mapbox-gl-js/src/shaders';
+const path = require('path');
+const shaders = require('../mapbox-gl-js/src/shaders');
const outputPath = 'src/mbgl/shaders';
require('./style-code');
-const vertexPrelude = fs.readFileSync(path.join(inputPath, '_prelude.vertex.glsl'));
-const fragmentPrelude = fs.readFileSync(path.join(inputPath, '_prelude.fragment.glsl'));
-
writeIfModified(path.join(outputPath, 'preludes.hpp'), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
#pragma once
@@ -33,84 +30,21 @@ namespace mbgl {
namespace shaders {
const char* vertexPrelude = R"MBGL_SHADER(
-${vertexPrelude}
+${shaders.prelude.vertexSource}
)MBGL_SHADER";
const char* fragmentPrelude = R"MBGL_SHADER(
-${fragmentPrelude}
+${shaders.prelude.fragmentSource}
)MBGL_SHADER";
} // namespace shaders
} // namespace mbgl
`);
-[
- 'circle',
- 'collision_box',
- 'debug',
- 'extrusion_texture',
- 'fill',
- 'fill_extrusion',
- 'fill_extrusion_pattern',
- 'fill_outline',
- 'fill_outline_pattern',
- 'fill_pattern',
- 'line',
- 'line_pattern',
- 'line_sdf',
- 'raster',
- 'symbol_icon',
- 'symbol_sdf'
-].forEach(function (shaderName) {
- const re = / *#pragma mapbox: ([\w]+) ([\w]+) ([\w]+) ([\w]+)/g;
- const fragmentPragmas = new Set();
-
- let fragmentSource = fs.readFileSync(path.join(inputPath, shaderName + '.fragment.glsl'), 'utf8');
- let vertexSource = fs.readFileSync(path.join(inputPath, shaderName + '.vertex.glsl'), 'utf8');
-
- fragmentSource = fragmentSource.replace(re, (match, operation, precision, type, name) => {
- fragmentPragmas.add(name);
- return operation === "define" ? `
-#ifndef HAS_UNIFORM_u_${name}
-varying ${precision} ${type} ${name};
-#else
-uniform ${precision} ${type} u_${name};
-#endif` : `
-#ifdef HAS_UNIFORM_u_${name}
- ${precision} ${type} ${name} = u_${name};
-#endif`;
- });
-
- vertexSource = vertexSource.replace(re, (match, operation, precision, type, name) => {
- const a_type = type === "float" ? "vec2" : "vec4";
- if (fragmentPragmas.has(name)) {
- return operation === "define" ? `
-#ifndef HAS_UNIFORM_u_${name}
-uniform lowp float a_${name}_t;
-attribute ${precision} ${a_type} a_${name};
-varying ${precision} ${type} ${name};
-#else
-uniform ${precision} ${type} u_${name};
-#endif` : `
-#ifndef HAS_UNIFORM_u_${name}
- ${name} = unpack_mix_${a_type}(a_${name}, a_${name}_t);
-#else
- ${precision} ${type} ${name} = u_${name};
-#endif`;
- } else {
- return operation === "define" ? `
-#ifndef HAS_UNIFORM_u_${name}
-uniform lowp float a_${name}_t;
-attribute ${precision} ${a_type} a_${name};
-#else
-uniform ${precision} ${type} u_${name};
-#endif` : `
-#ifndef HAS_UNIFORM_u_${name}
- ${precision} ${type} ${name} = unpack_mix_${a_type}(a_${name}, a_${name}_t);
-#else
- ${precision} ${type} ${name} = u_${name};
-#endif`;
- }
- });
+for (const key in shaders) {
+ if (key === 'prelude')
+ continue;
+
+ const shaderName = key.replace(/[A-Z]+/g, (match) => `_${match.toLowerCase()}`);
writeIfModified(path.join(outputPath, `${shaderName}.hpp`), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED.
@@ -139,13 +73,13 @@ namespace shaders {
const char* ${shaderName}::name = "${shaderName}";
const char* ${shaderName}::vertexSource = R"MBGL_SHADER(
-${vertexSource}
+${shaders[key].vertexSource}
)MBGL_SHADER";
const char* ${shaderName}::fragmentSource = R"MBGL_SHADER(
-${fragmentSource}
+${shaders[key].fragmentSource}
)MBGL_SHADER";
} // namespace shaders
} // namespace mbgl
`);
-});
+}
diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js
index b1d0ed28ec..443f0c7cc9 100644
--- a/scripts/generate-style-code.js
+++ b/scripts/generate-style-code.js
@@ -29,6 +29,9 @@ global.evaluatedType = function (property) {
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
+ if (/^(text|icon)-anchor$/.test(property.name)) {
+ return 'SymbolAnchorType';
+ }
if (/position/.test(property.name)) {
return 'Position';
}
@@ -183,8 +186,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/launch-c-xcode.in b/scripts/launch-c-xcode.in
new file mode 100644
index 0000000000..77a0c8aca3
--- /dev/null
+++ b/scripts/launch-c-xcode.in
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export CCACHE_CPP2=true
+exec "@C_LAUNCHER@" "${DEVELOPER_DIR}/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" "$@"
diff --git a/scripts/launch-c.in b/scripts/launch-c.in
new file mode 100644
index 0000000000..94555f32aa
--- /dev/null
+++ b/scripts/launch-c.in
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export CCACHE_CPP2=true
+exec "@C_LAUNCHER@" "$@"
diff --git a/scripts/launch-cxx-xcode.in b/scripts/launch-cxx-xcode.in
new file mode 100644
index 0000000000..7d4639ce6d
--- /dev/null
+++ b/scripts/launch-cxx-xcode.in
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export CCACHE_CPP2=true
+exec "@CXX_LAUNCHER@" "${DEVELOPER_DIR}/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++" "$@"
diff --git a/scripts/launch-cxx.in b/scripts/launch-cxx.in
new file mode 100644
index 0000000000..46671c1c64
--- /dev/null
+++ b/scripts/launch-cxx.in
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export CCACHE_CPP2=true
+exec "@CXX_LAUNCHER@" "$@"
diff --git a/scripts/lldb-types b/scripts/lldb-types
new file mode 100644
index 0000000000..fd6ae33830
--- /dev/null
+++ b/scripts/lldb-types
@@ -0,0 +1,15 @@
+type summary add "mbgl::CanonicalTileID" --summary-string "${var.z%u}/${var.x}/${var.y}"
+type summary add "mbgl::UnwrappedTileID" --summary-string "${var.canonical}+${var.wrap}"
+type summary add "mbgl::OverscaledTileID" --summary-string "${var.canonical}=>${var.overscaledZ%u}"
+
+type summary add -e -x "^mbgl::Range<.+>$" --summary-string "${var.min%d} ⇒ ${var.max%d}"
+type summary add -e -x "^mbgl::Rect<.+>$" --summary-string "Size: ${var.w%d}×${var.h%d} — Offset: ${var.x}/${var.y}"
+type summary add "mbgl::Size" --summary-string "${var.width}/${var.height}"
+type summary add "mbgl::LatLng" --summary-string "${var.lat}/${var.lon}"
+
+type summary add "mbgl::Color" --summary-string "${var.r}, ${var.g}, ${var.b}, ${var.a}"
+
+type summary add -e -x "^mbgl::Point<.+>$" --summary-string "${var.x}/${var.y}"
+type summary add -e -x "^mapbox::geometry::point<.+>$" --summary-string "${var.x}/${var.y}"
+
+type summary add -e -x "^mbgl::optional<.+>$" --python-script "return valobj.GetChildAtIndex(0).GetChildAtIndex(0).GetChildAtIndex(1).GetValue() if valobj.GetChildMemberWithName('__engaged_').unsigned > 0 else '<empty>'"
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/scripts/valgrind.sup b/scripts/valgrind.sup
index d8870536ac..09e2f6685c 100644
--- a/scripts/valgrind.sup
+++ b/scripts/valgrind.sup
@@ -1,142 +1,19 @@
{
- Graphics driver buffers
- Memcheck:Leak
- ...
- obj:*/r600_dri.*
- obj:*/r600_dri.*
- obj:*/r600_dri.*
- ...
-}
-{
- Graphics driver buffers
- Memcheck:Leak
- ...
- obj:*/i965_dri.*
- obj:*/i965_dri.*
- obj:*/i965_dri.*
- ...
-}
-{
- Graphics driver buffers
- Memcheck:Leak
- ...
- obj:*/nouveau_dri.*
- obj:*/nouveau_dri.*
- obj:*/nouveau_dri.*
- ...
-}
-{
- Ubuntu 16.04 - GLFW
- Memcheck:Leak
- ...
- fun:glfwInit
- ...
-}
-{
- Ubuntu 16.04 - GLFW
+ Graphics driver bugs
Memcheck:Cond
...
- fun:glfwInit
- ...
-}
-{
- Ubuntu 16.04 - Qt + glib
- Memcheck:Cond
- fun:g_utf8_offset_to_pointer
- ...
-}
-{
- Ubuntu 16.04 - Qt + glib
- Memcheck:Cond
- ...
- fun:g_signal_emit_valist
- ...
- fun:*QApplicationPrivate*
- ...
-}
-{
- Ubuntu 16.04 - Qt + X11
- Memcheck:Param
- writev(vector[...])
- ...
- obj:*/libxcb.*
- fun:xcb_flush
- ...
-}
-{
- Ubuntu 16.04 - Qt + X11
- Memcheck:Leak
- ...
- fun:_XmbTextListToTextProperty
- ...
- fun:*QWidget*setVisible*
- ...
-}
-{
- Ubuntu 16.04 - Qt + Fontconfig
- Memcheck:Leak
- ...
- obj:*/libfontconfig.*
- obj:*/libfontconfig.*
- ...
- fun:FcFontRenderPrepare
- ...
-}
-{
- Ubuntu 16.04 - Qt + Fontconfig
- Memcheck:Leak
- ...
- obj:*/libfontconfig.*
- obj:*/libfontconfig.*
- ...
- fun:FcPatternAddInteger
- ...
-}
-{
- Ubuntu 16.04 - Qt + Fontconfig
- Memcheck:Leak
- ...
- obj:*/libfontconfig.*
- obj:*/libfontconfig.*
- ...
- fun:FcConfigParseAndLoad
- ...
-}
-{
- Ubuntu 16.04 - Qt + Dbus
- Memcheck:Leak
- ...
- fun:dbus_connection_send_with_reply
- ...
- fun:*QWidget*
-}
-{
- Ubuntu 16.04 - Qt + Dbus
- Memcheck:Leak
- ...
- fun:px_proxy_factory_get_proxies
- obj:*/libQt5Network.*
-}
-{
- Ubuntu 16.04 - Qt
- Memcheck:Leak
- ...
- fun:*QGuiApplicationPrivate*createEventDispatcher*
- fun:*QCoreApplication*init*
- ...
-}
-{
- Ubuntu 16.04 - Qt
- Memcheck:Leak
- ...
- fun:*QWidget*setWindowTitle*
+ obj:*/swrast_dri.*
+ obj:*/swrast_dri.*
+ obj:*/swrast_dri.*
...
}
{
- Ubuntu 16.04 - Qt
- Memcheck:Leak
+ Graphics driver bugs
+ Memcheck:Value8
...
- fun:*QNetworkConfigurationManagerPrivate*updateConfigurations*
+ obj:*/swrast_dri.*
+ obj:*/swrast_dri.*
+ obj:*/swrast_dri.*
...
}
{
@@ -156,118 +33,42 @@
obj:*
}
{
- Ubuntu 16.04 - Mysterious leak when running utests
- Memcheck:Leak
- fun:malloc
- obj:*
- obj:*
- obj:*
- obj:*
- obj:*
- obj:*
- obj:*
- obj:*
- obj:*
- obj:*
- obj:*
-}
-{
- Ubuntu 14.04 - Travis CI bot using swrast
- Memcheck:Cond
- fun:do_stencil_test
- fun:_swrast_stencil_and_ztest_span
- fun:_swrast_write_rgba_span
- fun:draw_wide_line
- ...
-}
-{
- Ubuntu 14.04 - Travis + Qt5
- Memcheck:Leak
- ...
- obj:*/libQt5Core.*
- ...
-}
-{
- Ubuntu 14.04 - Travis + Qt5
+ Qt5
Memcheck:Cond
...
obj:*/libQt5Core.*
...
}
{
- Ubuntu 14.04 - Travis + Qt5
+ Qt5
Memcheck:Value8
...
obj:*/libQt5Core.*
...
}
{
- Ubuntu 14.04 - Travis + Qt5
- Memcheck:Leak
- ...
- obj:*/libQt5Gui.*
- ...
-}
-{
- Ubuntu 14.04 - Travis + Qt5
+ Qt5
Memcheck:Cond
...
obj:*/libQt5Gui.*
...
}
{
- Ubuntu 14.04 - Travis + Qt5
+ Qt5
Memcheck:Value8
...
obj:*/libQt5Gui.*
...
}
{
- Ubuntu 14.04 - Travis + mesa 13.0.0-glx
- Memcheck:Cond
- ...
- obj:*/mesa/libGL.so.*
- ...
-}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.0-glx
- Memcheck:Value8
- ...
- obj:*/mesa/libGL.so.*
- ...
-}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.0-glx
- Memcheck:Cond
- ...
- fun:do_rasterize_bin
- ...
-}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.0-glx
- Memcheck:Value8
- ...
- fun:do_rasterize_bin
- ...
-}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.0-glx
+ mapbox::pixelmatch
Memcheck:Cond
...
fun:_ZN6mapbox10pixelmatchEPKhS1_mmPhdb
...
}
{
- Ubuntu 14.04 - Travis + mesa 13.0.0-glx
- Memcheck:Param
- write(buf)
- ...
- obj:*/libc-*
- fun:_ZN4mbgl4util10write_fileERKSsS2_
- ...
-}
-{
- Ubuntu 16.04 - CircleCI + mesa 13.0.4
+ util::write_file
Memcheck:Param
write(buf)
...
@@ -275,74 +76,3 @@
fun:_ZN4mbgl4util10write_fileERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES8_
...
}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.3
- Memcheck:Cond
- fun:_ZN12_GLOBAL__N_117PeepholeOptimizer20runOnMachineFunctionERN4llvm15MachineFunctionE
- fun:_ZN4llvm19MachineFunctionPass13runOnFunctionERNS_8FunctionE
- fun:_ZN4llvm13FPPassManager13runOnFunctionERNS_8FunctionE
- fun:_ZN4llvm13FPPassManager11runOnModuleERNS_6ModuleE
- fun:_ZN4llvm6legacy15PassManagerImpl3runERNS_6ModuleE
- fun:_ZN4llvm5MCJIT10emitObjectEPNS_6ModuleE
- fun:_ZN4llvm5MCJIT21generateCodeForModuleEPNS_6ModuleE
- fun:_ZN4llvm5MCJIT14finalizeObjectEv
- fun:LLVMGetPointerToGlobal
- fun:gallivm_jit_function
- fun:generate_variant
- fun:llvmpipe_update_fs
-}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.3
- Memcheck:Cond
- fun:_ZNK4llvm12X86InstrInfo13reMaterializeERNS_17MachineBasicBlockENS1_15bundle_iteratorINS_12MachineInstrENS_14ilist_iteratorIS4_EEEEjjPKS4_RKNS_18TargetRegisterInfoE
- fun:_ZN12_GLOBAL__N_117RegisterCoalescer23reMaterializeTrivialDefERKN4llvm13CoalescerPairEPNS1_12MachineInstrERb
- fun:_ZN12_GLOBAL__N_117RegisterCoalescer8joinCopyEPN4llvm12MachineInstrERb
- fun:_ZN12_GLOBAL__N_117RegisterCoalescer20runOnMachineFunctionERN4llvm15MachineFunctionE
- fun:_ZN4llvm19MachineFunctionPass13runOnFunctionERNS_8FunctionE
- fun:_ZN4llvm13FPPassManager13runOnFunctionERNS_8FunctionE
- fun:_ZN4llvm13FPPassManager11runOnModuleERNS_6ModuleE
- fun:_ZN4llvm6legacy15PassManagerImpl3runERNS_6ModuleE
- fun:_ZN4llvm5MCJIT10emitObjectEPNS_6ModuleE
- fun:_ZN4llvm5MCJIT21generateCodeForModuleEPNS_6ModuleE
- fun:_ZN4llvm5MCJIT14finalizeObjectEv
- fun:LLVMGetPointerToGlobal
-}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.3
- Memcheck:Cond
- fun:_ZNK4llvm12X86InstrInfo13reMaterializeERNS_17MachineBasicBlockENS1_15bundle_iteratorINS_12MachineInstrENS_14ilist_iteratorIS4_EEEEjjPKS4_RKNS_18TargetRegisterInfoE
- fun:_ZN4llvm13LiveRangeEdit15rematerializeAtERNS_17MachineBasicBlockENS1_15bundle_iteratorINS_12MachineInstrENS_14ilist_iteratorIS4_EEEEjRKNS0_5RematERKNS_18TargetRegisterInfoEb
- fun:_ZN12_GLOBAL__N_113InlineSpiller5spillERN4llvm13LiveRangeEditE
- fun:_ZN12_GLOBAL__N_18RAGreedy17selectOrSplitImplERN4llvm12LiveIntervalERNS1_15SmallVectorImplIjEERNS1_8SmallSetIjLj16ESt4lessIjEEEj
- fun:_ZN12_GLOBAL__N_18RAGreedy13selectOrSplitERN4llvm12LiveIntervalERNS1_15SmallVectorImplIjEE
- fun:_ZN4llvm12RegAllocBase16allocatePhysRegsEv
- fun:_ZN12_GLOBAL__N_18RAGreedy20runOnMachineFunctionERN4llvm15MachineFunctionE
- fun:_ZN4llvm19MachineFunctionPass13runOnFunctionERNS_8FunctionE
- fun:_ZN4llvm13FPPassManager13runOnFunctionERNS_8FunctionE
- fun:_ZN4llvm13FPPassManager11runOnModuleERNS_6ModuleE
- fun:_ZN4llvm6legacy15PassManagerImpl3runERNS_6ModuleE
- fun:_ZN4llvm5MCJIT10emitObjectEPNS_6ModuleE
-}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.3
- Memcheck:Cond
- fun:_ZNK4llvm12X86InstrInfo13reMaterializeERNS_17MachineBasicBlockENS1_15bundle_iteratorINS_12MachineInstrENS_14ilist_iteratorIS4_EEEEjjPKS4_RKNS_18TargetRegisterInfoE
- fun:_ZN4llvm13LiveRangeEdit15rematerializeAtERNS_17MachineBasicBlockENS1_15bundle_iteratorINS_12MachineInstrENS_14ilist_iteratorIS4_EEEEjRKNS0_5RematERKNS_18TargetRegisterInfoEb
- fun:_ZN4llvm11SplitEditor13defFromParentEjPNS_6VNInfoENS_9SlotIndexERNS_17MachineBasicBlockENS4_15bundle_iteratorINS_12MachineInstrENS_14ilist_iteratorIS7_EEEE
- fun:_ZN4llvm11SplitEditor16splitRegOutBlockERKNS_13SplitAnalysis9BlockInfoEjNS_9SlotIndexE
- fun:_ZN12_GLOBAL__N_18RAGreedy13doRegionSplitERN4llvm12LiveIntervalEjbRNS1_15SmallVectorImplIjEE
- fun:_ZN12_GLOBAL__N_18RAGreedy17selectOrSplitImplERN4llvm12LiveIntervalERNS1_15SmallVectorImplIjEERNS1_8SmallSetIjLj16ESt4lessIjEEEj
- fun:_ZN12_GLOBAL__N_18RAGreedy13selectOrSplitERN4llvm12LiveIntervalERNS1_15SmallVectorImplIjEE
- fun:_ZN4llvm12RegAllocBase16allocatePhysRegsEv
- fun:_ZN12_GLOBAL__N_18RAGreedy20runOnMachineFunctionERN4llvm15MachineFunctionE
- fun:_ZN4llvm19MachineFunctionPass13runOnFunctionERNS_8FunctionE
- fun:_ZN4llvm13FPPassManager13runOnFunctionERNS_8FunctionE
- fun:_ZN4llvm13FPPassManager11runOnModuleERNS_6ModuleE
-}
-{
- Ubuntu 14.04 - Travis + mesa 13.0.3
- Memcheck:Leak
- match-leak-kinds: definite
- fun:_Znwm
- ...
-}
diff --git a/src/mbgl/actor/scheduler.cpp b/src/mbgl/actor/scheduler.cpp
new file mode 100644
index 0000000000..d7cdb2737b
--- /dev/null
+++ b/src/mbgl/actor/scheduler.cpp
@@ -0,0 +1,19 @@
+#include <mbgl/actor/scheduler.hpp>
+#include <mbgl/util/thread_local.hpp>
+
+namespace mbgl {
+
+static auto& current() {
+ static util::ThreadLocal<Scheduler> scheduler;
+ return scheduler;
+};
+
+void Scheduler::SetCurrent(Scheduler* scheduler) {
+ current().set(scheduler);
+}
+
+Scheduler* Scheduler::GetCurrent() {
+ return current().get();
+}
+
+} //namespace mbgl
diff --git a/src/mbgl/algorithm/generate_clip_ids.cpp b/src/mbgl/algorithm/generate_clip_ids.cpp
index 74e0ee242f..287d2a408e 100644
--- a/src/mbgl/algorithm/generate_clip_ids.cpp
+++ b/src/mbgl/algorithm/generate_clip_ids.cpp
@@ -31,14 +31,14 @@ bool ClipIDGenerator::Leaf::operator==(const Leaf& other) const {
return children == other.children;
}
-std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const {
- std::map<UnwrappedTileID, ClipID> stencils;
+std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getClipIDs() const {
+ std::map<UnwrappedTileID, ClipID> clipIDs;
// Merge everything.
for (auto& pair : pool) {
auto& id = pair.first;
auto& leaf = pair.second;
- auto res = stencils.emplace(id, leaf.clip);
+ auto res = clipIDs.emplace(id, leaf.clip);
if (!res.second) {
// Merge with the existing ClipID when there was already an element with the
// same tile ID.
@@ -46,14 +46,14 @@ std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const {
}
}
- for (auto it = stencils.begin(); it != stencils.end(); ++it) {
+ for (auto it = clipIDs.begin(); it != clipIDs.end(); ++it) {
auto& childId = it->first;
auto& childClip = it->second;
// Loop through all preceding stencils, and find all parents.
for (auto parentIt = std::reverse_iterator<decltype(it)>(it);
- parentIt != stencils.rend(); ++parentIt) {
+ parentIt != clipIDs.rend(); ++parentIt) {
auto& parentId = parentIt->first;
if (childId.isChildOf(parentId)) {
// Once we have a parent, we add the bits that this ID hasn't set yet.
@@ -66,11 +66,11 @@ std::map<UnwrappedTileID, ClipID> ClipIDGenerator::getStencils() const {
}
// Remove tiles that are entirely covered by children.
- util::erase_if(stencils, [&](const auto& stencil) {
- return algorithm::coveredByChildren(stencil.first, stencils);
+ util::erase_if(clipIDs, [&](const auto& stencil) {
+ return algorithm::coveredByChildren(stencil.first, clipIDs);
});
- return stencils;
+ return clipIDs;
}
} // namespace algorithm
diff --git a/src/mbgl/algorithm/generate_clip_ids.hpp b/src/mbgl/algorithm/generate_clip_ids.hpp
index d917b398af..adcf87a72a 100644
--- a/src/mbgl/algorithm/generate_clip_ids.hpp
+++ b/src/mbgl/algorithm/generate_clip_ids.hpp
@@ -3,9 +3,9 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/clip_id.hpp>
-#include <unordered_set>
+#include <set>
#include <vector>
-#include <unordered_map>
+#include <map>
namespace mbgl {
namespace algorithm {
@@ -17,18 +17,18 @@ private:
void add(const CanonicalTileID &p);
bool operator==(const Leaf &other) const;
- std::unordered_set<CanonicalTileID> children;
+ std::set<CanonicalTileID> children;
ClipID& clip;
};
uint8_t bit_offset = 0;
- std::unordered_multimap<UnwrappedTileID, Leaf> pool;
+ std::multimap<UnwrappedTileID, Leaf> pool;
public:
- template <typename Renderables>
- void update(Renderables& renderables);
+ template <typename Renderable>
+ void update(std::vector<std::reference_wrapper<Renderable>> renderables);
- std::map<UnwrappedTileID, ClipID> getStencils() const;
+ std::map<UnwrappedTileID, ClipID> getClipIDs() const;
};
} // namespace algorithm
diff --git a/src/mbgl/algorithm/generate_clip_ids_impl.hpp b/src/mbgl/algorithm/generate_clip_ids_impl.hpp
index d63ba27b6b..6a316dcdad 100644
--- a/src/mbgl/algorithm/generate_clip_ids_impl.hpp
+++ b/src/mbgl/algorithm/generate_clip_ids_impl.hpp
@@ -7,15 +7,17 @@
namespace mbgl {
namespace algorithm {
-template <typename Renderables>
-void ClipIDGenerator::update(Renderables& renderables) {
+template <typename Renderable>
+void ClipIDGenerator::update(std::vector<std::reference_wrapper<Renderable>> renderables) {
std::size_t size = 0;
+ std::sort(renderables.begin(), renderables.end(),
+ [](const auto& a, const auto& b) { return a.get().id < b.get().id; });
+
const auto end = renderables.end();
for (auto it = renderables.begin(); it != end; it++) {
- auto& tileID = it->first;
- auto& renderable = it->second;
- if (!renderable.used) {
+ auto& renderable = it->get();
+ if (!renderable.used || !renderable.needsClipping) {
continue;
}
@@ -28,17 +30,17 @@ void ClipIDGenerator::update(Renderables& renderables) {
// can never be children of the current wrap.
auto child_it = std::next(it);
const auto children_end = std::lower_bound(
- child_it, end, UnwrappedTileID{ static_cast<int16_t>(tileID.wrap + 1), { 0, 0, 0 } },
- [](auto& a, auto& b) { return a.first < b; });
+ child_it, end, UnwrappedTileID{ static_cast<int16_t>(renderable.id.wrap + 1), { 0, 0, 0 } },
+ [](auto& a, auto& b) { return a.get().id < b; });
for (; child_it != children_end; ++child_it) {
- auto& childTileID = child_it->first;
- if (childTileID.isChildOf(tileID)) {
+ auto& childTileID = child_it->get().id;
+ if (childTileID.isChildOf(it->get().id)) {
leaf.add(childTileID.canonical);
}
}
// Find a leaf with matching children.
- for (auto its = pool.equal_range(tileID); its.first != its.second; ++its.first) {
+ for (auto its = pool.equal_range(renderable.id); its.first != its.second; ++its.first) {
auto& existing = its.first->second;
if (existing == leaf) {
leaf.clip = existing.clip;
@@ -50,7 +52,7 @@ void ClipIDGenerator::update(Renderables& renderables) {
size++;
}
- pool.emplace(tileID, std::move(leaf));
+ pool.emplace(renderable.id, std::move(leaf));
}
if (size > 0) {
@@ -60,8 +62,8 @@ void ClipIDGenerator::update(Renderables& renderables) {
// We are starting our count with 1 since we need at least 1 bit set to distinguish between
// areas without any tiles whatsoever and the current area.
uint8_t count = 1;
- for (auto& pair : renderables) {
- auto& renderable = pair.second;
+ for (auto& it : renderables) {
+ auto& renderable = it.get();
if (!renderable.used) {
continue;
}
diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp
index a9c348b538..c583b6b2b6 100644
--- a/src/mbgl/algorithm/update_renderables.hpp
+++ b/src/mbgl/algorithm/update_renderables.hpp
@@ -1,8 +1,8 @@
#pragma once
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/tile/tile_necessity.hpp>
#include <mbgl/util/range.hpp>
-#include <mbgl/storage/resource.hpp>
#include <unordered_set>
@@ -31,7 +31,7 @@ void updateRenderables(GetTileFn getTile,
assert(idealRenderTileID.canonical.z <= zoomRange.max);
assert(dataTileZoom >= idealRenderTileID.canonical.z);
- const OverscaledTileID idealDataTileID(dataTileZoom, idealRenderTileID.canonical);
+ const OverscaledTileID idealDataTileID(dataTileZoom, idealRenderTileID.wrap, idealRenderTileID.canonical);
auto tile = getTile(idealDataTileID);
if (!tile) {
tile = createTile(idealDataTileID);
@@ -40,15 +40,15 @@ void updateRenderables(GetTileFn getTile,
// if (source has the tile and bucket is loaded) {
if (tile->isRenderable()) {
- retainTile(*tile, Resource::Necessity::Required);
+ retainTile(*tile, TileNecessity::Required);
renderTile(idealRenderTileID, *tile);
} else {
// We are now attempting to load child and parent tiles.
- bool parentHasTriedOptional = tile->hasTriedOptional();
+ bool parentHasTriedOptional = tile->hasTriedCache();
bool parentIsLoaded = tile->isLoaded();
// The tile isn't loaded yet, but retain it anyway because it's an ideal tile.
- retainTile(*tile, Resource::Necessity::Required);
+ retainTile(*tile, TileNecessity::Required);
covered = true;
overscaledZ = dataTileZoom + 1;
if (overscaledZ > zoomRange.max) {
@@ -56,7 +56,7 @@ void updateRenderables(GetTileFn getTile,
const auto childDataTileID = idealDataTileID.scaledTo(overscaledZ);
tile = getTile(childDataTileID);
if (tile && tile->isRenderable()) {
- retainTile(*tile, Resource::Necessity::Optional);
+ retainTile(*tile, TileNecessity::Optional);
renderTile(idealRenderTileID, *tile);
} else {
covered = false;
@@ -64,11 +64,11 @@ void updateRenderables(GetTileFn getTile,
} else {
// Check all four actual child tiles.
for (const auto& childTileID : idealDataTileID.canonical.children()) {
- const OverscaledTileID childDataTileID(overscaledZ, childTileID);
+ const OverscaledTileID childDataTileID(overscaledZ, idealRenderTileID.wrap, childTileID);
tile = getTile(childDataTileID);
if (tile && tile->isRenderable()) {
- retainTile(*tile, Resource::Necessity::Optional);
- renderTile(childDataTileID.unwrapTo(idealRenderTileID.wrap), *tile);
+ retainTile(*tile, TileNecessity::Optional);
+ renderTile(childDataTileID.toUnwrapped(), *tile);
} else {
// At least one child tile doesn't exist, so we are going to look for
// parents as well.
@@ -81,8 +81,7 @@ void updateRenderables(GetTileFn getTile,
// We couldn't find child tiles that entirely cover the ideal tile.
for (overscaledZ = dataTileZoom - 1; overscaledZ >= zoomRange.min; --overscaledZ) {
const auto parentDataTileID = idealDataTileID.scaledTo(overscaledZ);
- const auto parentRenderTileID =
- parentDataTileID.unwrapTo(idealRenderTileID.wrap);
+ const auto parentRenderTileID = parentDataTileID.toUnwrapped();
if (checked.find(parentRenderTileID) != checked.end()) {
// Break parent tile ascent, this route has been checked by another child
@@ -98,12 +97,19 @@ void updateRenderables(GetTileFn getTile,
}
if (tile) {
- retainTile(*tile, parentIsLoaded ? Resource::Necessity::Required
- : Resource::Necessity::Optional);
+ if (!parentIsLoaded) {
+ // We haven't completed loading the child, so we only do an optional
+ // (cache) request in an attempt to quickly load data that we can show.
+ retainTile(*tile, TileNecessity::Optional);
+ } else {
+ // Now that we've checked the child and know for sure that we can't load
+ // it, we attempt to load the parent from the network.
+ retainTile(*tile, TileNecessity::Required);
+ }
// Save the current values, since they're the parent of the next iteration
// of the parent tile ascent loop.
- parentHasTriedOptional = tile->hasTriedOptional();
+ parentHasTriedOptional = tile->hasTriedCache();
parentIsLoaded = tile->isLoaded();
if (tile->isRenderable()) {
diff --git a/src/mbgl/algorithm/update_tile_masks.hpp b/src/mbgl/algorithm/update_tile_masks.hpp
new file mode 100644
index 0000000000..a7840cd163
--- /dev/null
+++ b/src/mbgl/algorithm/update_tile_masks.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+#include <mbgl/renderer/tile_mask.hpp>
+
+#include <vector>
+#include <functional>
+#include <algorithm>
+
+namespace mbgl {
+namespace algorithm {
+
+namespace {
+
+template <typename Renderable>
+void computeTileMasks(
+ const CanonicalTileID& root,
+ const UnwrappedTileID ref,
+ typename std::vector<std::reference_wrapper<Renderable>>::const_iterator it,
+ const typename std::vector<std::reference_wrapper<Renderable>>::const_iterator end,
+ TileMask& mask) {
+ // If the reference or any of its children is found in the list, we need to recurse.
+ for (; it != end; ++it) {
+ auto& renderable = it->get();
+ if (!renderable.used) {
+ continue;
+ }
+ if (ref == renderable.id) {
+ // The current tile is masked out, so we don't need to add them to the mask set.
+ return;
+ } else if (renderable.id.isChildOf(ref)) {
+ // There's at least one child tile that is masked out, so recursively descend.
+ for (const auto& child : ref.children()) {
+ computeTileMasks<Renderable>(root, child, it, end, mask);
+ }
+ return;
+ }
+ }
+
+ // We couldn't find a child, so it's definitely a masked part.
+ // Compute the difference between the root tile ID and the reference tile ID, since TileMask
+ // elements are always relative (see below for explanation).
+ const uint8_t diffZ = ref.canonical.z - root.z;
+ mask.emplace(diffZ, ref.canonical.x - (root.x << diffZ), ref.canonical.y - (root.y << diffZ));
+}
+
+} // namespace
+
+// Updates the TileMasks for all renderables. Renderables are objects that have an UnwrappedTileID
+// property indicating where they should be rendered on the screen. A TileMask describes all regions
+// within that tile that are *not* covered by other Renderables.
+// Example: Renderables in our list are 2/1/3, 3/3/6, and 4/5/13. The schematic for creating the
+// TileMask for 2/1/3 looks like this:
+//
+// ┌────────┬────────┬─────────────────┐
+// │ │ │#################│
+// │ 4/4/12 │ 4/5/12 │#################│
+// │ │ │#################│
+// ├──────3/2/6──────┤#####3/3/6#######│
+// │ │########│#################│
+// │ 4/4/13 │#4/5/13#│#################│
+// │ │########│#################│
+// ├────────┴──────2/1/3───────────────┤
+// │ │ │
+// │ │ │
+// │ │ │
+// │ 3/2/7 │ 3/3/7 │
+// │ │ │
+// │ │ │
+// │ │ │
+// └─────────────────┴─────────────────┘
+//
+// The TileMask for 2/1/3 thus consists of the tiles 4/4/12, 4/5/12, 4/4/13, 3/2/7, and 3/3/7,
+// but it does *not* include 4/5/13, and 3/3/6, since these are other Renderables.
+// A TileMask always contains TileIDs *relative* to the tile it is generated for, so 2/1/3 is
+// "subtracted" from these TileIDs. The final TileMask for 2/1/3 will thus be:
+//
+// ┌────────┬────────┬─────────────────┐
+// │ │ │#################│
+// │ 2/0/0 │ 2/1/0 │#################│
+// │ │ │#################│
+// ├────────┼────────┤#################│
+// │ │########│#################│
+// │ 2/0/1 │########│#################│
+// │ │########│#################│
+// ├────────┴────────┼─────────────────┤
+// │ │ │
+// │ │ │
+// │ │ │
+// │ 1/0/1 │ 1/1/1 │
+// │ │ │
+// │ │ │
+// │ │ │
+// └─────────────────┴─────────────────┘
+//
+// Only other Renderables that are *children* of the Renderable we are generating the mask for will
+// be considered. For example, adding a Renderable with TileID 4/8/13 won't affect the TileMask for
+// 2/1/3, since it is not a descendant of it.
+template <typename Renderable>
+void updateTileMasks(std::vector<std::reference_wrapper<Renderable>> renderables) {
+ std::sort(renderables.begin(), renderables.end(),
+ [](const Renderable& a, const Renderable& b) { return a.id < b.id; });
+
+ TileMask mask;
+ const auto end = renderables.end();
+ for (auto it = renderables.begin(); it != end; it++) {
+ auto& renderable = it->get();
+ if (!renderable.used) {
+ continue;
+ }
+ // Try to add all remaining ids as children. We sorted the tile list
+ // by z earlier, so all preceding items cannot be children of the current
+ // tile. We also compute the lower bound of the next wrap, because items of the next wrap
+ // can never be children of the current wrap.
+ auto child_it = std::next(it);
+ const auto children_end = std::lower_bound(
+ child_it, end,
+ UnwrappedTileID{ static_cast<int16_t>(renderable.id.wrap + 1), { 0, 0, 0 } },
+ [](auto& a, auto& b) { return a.get().id < b; });
+
+ mask.clear();
+ computeTileMasks<Renderable>(renderable.id.canonical, renderable.id, child_it, children_end,
+ mask);
+ renderable.setMask(std::move(mask));
+ }
+}
+
+} // namespace algorithm
+} // namespace mbgl
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp
index a69dba1bf2..b94b0a1bce 100644
--- a/src/mbgl/annotation/annotation_manager.cpp
+++ b/src/mbgl/annotation/annotation_manager.cpp
@@ -4,6 +4,7 @@
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/annotation/line_annotation_impl.hpp>
#include <mbgl/annotation/fill_annotation_impl.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
@@ -17,102 +18,103 @@ using namespace style;
const std::string AnnotationManager::SourceID = "com.mapbox.annotations";
const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points";
+const std::string AnnotationManager::ShapeLayerID = "com.mapbox.annotations.shape.";
+
+AnnotationManager::AnnotationManager(Style& style_)
+ : style(style_) {
+};
-AnnotationManager::AnnotationManager() = default;
AnnotationManager::~AnnotationManager() = default;
-AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::setStyle(Style& style_) {
+ style = style_;
+}
+
+void AnnotationManager::onStyleLoaded() {
+ updateStyle();
+}
+
+AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation) {
std::lock_guard<std::mutex> lock(mutex);
AnnotationID id = nextID++;
Annotation::visit(annotation, [&] (const auto& annotation_) {
- this->add(id, annotation_, maxZoom);
+ this->add(id, annotation_);
});
+ dirty = true;
return id;
}
-Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
+bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation) {
std::lock_guard<std::mutex> lock(mutex);
- return Annotation::visit(annotation, [&] (const auto& annotation_) {
- return this->update(id, annotation_, maxZoom);
+ Annotation::visit(annotation, [&] (const auto& annotation_) {
+ this->update(id, annotation_);
});
+ return dirty;
}
void AnnotationManager::removeAnnotation(const AnnotationID& id) {
std::lock_guard<std::mutex> lock(mutex);
remove(id);
+ dirty = true;
}
-void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t) {
+void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation) {
auto impl = std::make_shared<SymbolAnnotationImpl>(id, annotation);
symbolTree.insert(impl);
symbolAnnotations.emplace(id, impl);
}
-void AnnotationManager::add(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::add(const AnnotationID& id, const LineAnnotation& annotation) {
ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
- std::make_unique<LineAnnotationImpl>(id, annotation, maxZoom)).first->second;
- obsoleteShapeAnnotationLayers.erase(impl.layerID);
+ std::make_unique<LineAnnotationImpl>(id, annotation)).first->second;
+ impl.updateStyle(*style.get().impl);
}
-void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annotation) {
ShapeAnnotationImpl& impl = *shapeAnnotations.emplace(id,
- std::make_unique<FillAnnotationImpl>(id, annotation, maxZoom)).first->second;
- obsoleteShapeAnnotationLayers.erase(impl.layerID);
+ std::make_unique<FillAnnotationImpl>(id, annotation)).first->second;
+ impl.updateStyle(*style.get().impl);
}
-Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) {
- Update result = Update::Nothing;
-
+void AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation) {
auto it = symbolAnnotations.find(id);
if (it == symbolAnnotations.end()) {
assert(false); // Attempt to update a non-existent symbol annotation
- return result;
+ return;
}
const SymbolAnnotation& existing = it->second->annotation;
- if (existing.geometry != annotation.geometry) {
- result |= Update::AnnotationData;
- }
-
- if (existing.icon != annotation.icon) {
- result |= Update::AnnotationData | Update::AnnotationStyle;
- }
+ if (existing.geometry != annotation.geometry || existing.icon != annotation.icon) {
+ dirty = true;
- if (result != Update::Nothing) {
- removeAndAdd(id, annotation, maxZoom);
+ remove(id);
+ add(id, annotation);
}
-
- return result;
}
-Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation) {
auto it = shapeAnnotations.find(id);
if (it == shapeAnnotations.end()) {
assert(false); // Attempt to update a non-existent shape annotation
- return Update::Nothing;
+ return;
}
- removeAndAdd(id, annotation, maxZoom);
- return Update::AnnotationData | Update::AnnotationStyle;
+ shapeAnnotations.erase(it);
+ add(id, annotation);
+ dirty = true;
}
-Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) {
+void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation) {
auto it = shapeAnnotations.find(id);
if (it == shapeAnnotations.end()) {
assert(false); // Attempt to update a non-existent shape annotation
- return Update::Nothing;
+ return;
}
- removeAndAdd(id, annotation, maxZoom);
- return Update::AnnotationData | Update::AnnotationStyle;
-}
-
-void AnnotationManager::removeAndAdd(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) {
- remove(id);
- Annotation::visit(annotation, [&] (const auto& annotation_) {
- this->add(id, annotation_, maxZoom);
- });
+ shapeAnnotations.erase(it);
+ add(id, annotation);
+ dirty = true;
}
void AnnotationManager::remove(const AnnotationID& id) {
@@ -120,8 +122,9 @@ void AnnotationManager::remove(const AnnotationID& id) {
symbolTree.remove(symbolAnnotations.at(id));
symbolAnnotations.erase(id);
} else if (shapeAnnotations.find(id) != shapeAnnotations.end()) {
- obsoleteShapeAnnotationLayers.insert(shapeAnnotations.at(id)->layerID);
- shapeAnnotations.erase(id);
+ auto it = shapeAnnotations.find(id);
+ *style.get().impl->removeLayer(it->second->layerID);
+ shapeAnnotations.erase(it);
} else {
assert(false); // Should never happen
}
@@ -149,11 +152,11 @@ std::unique_ptr<AnnotationTileData> AnnotationManager::getTileData(const Canonic
return tileData;
}
-void AnnotationManager::updateStyle(Style::Impl& style) {
+void AnnotationManager::updateStyle() {
// Create annotation source, point layer, and point bucket. We do everything via Style::Impl
// because we don't want annotation mutations to trigger Style::Impl::styleMutated to be set.
- if (!style.getSource(SourceID)) {
- style.addSource(std::make_unique<AnnotationSource>());
+ if (!style.get().impl->getSource(SourceID)) {
+ style.get().impl->addSource(std::make_unique<AnnotationSource>());
std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(PointLayerID, SourceID);
@@ -162,13 +165,13 @@ void AnnotationManager::updateStyle(Style::Impl& style) {
layer->setIconAllowOverlap(true);
layer->setIconIgnorePlacement(true);
- style.addLayer(std::move(layer));
+ style.get().impl->addLayer(std::move(layer));
}
std::lock_guard<std::mutex> lock(mutex);
for (const auto& shape : shapeAnnotations) {
- shape.second->updateStyle(style);
+ shape.second->updateStyle(*style.get().impl);
}
for (const auto& image : images) {
@@ -178,29 +181,17 @@ void AnnotationManager::updateStyle(Style::Impl& style) {
// of which images need to be added because we don't know if the style is the same
// instance as in the last updateStyle call. If it's a new style, we need to add all
// images.)
- style.addImage(std::make_unique<style::Image>(image.second));
- }
-
- for (const auto& layer : obsoleteShapeAnnotationLayers) {
- if (style.getLayer(layer)) {
- style.removeLayer(layer);
- }
- }
-
- for (const auto& image : obsoleteImages) {
- if (style.getImage(image)) {
- style.removeImage(image);
- }
+ style.get().impl->addImage(std::make_unique<style::Image>(image.second));
}
-
- obsoleteShapeAnnotationLayers.clear();
- obsoleteImages.clear();
}
void AnnotationManager::updateData() {
std::lock_guard<std::mutex> lock(mutex);
- for (auto& tile : tiles) {
- tile->setData(getTileData(tile->id.canonical));
+ if (dirty) {
+ for (auto& tile : tiles) {
+ tile->setData(getTileData(tile->id.canonical));
+ }
+ dirty = false;
}
}
@@ -225,16 +216,16 @@ void AnnotationManager::addImage(std::unique_ptr<style::Image> image) {
std::lock_guard<std::mutex> lock(mutex);
const std::string id = prefixedImageID(image->getID());
images.erase(id);
- images.emplace(id,
- style::Image(id, image->getImage().clone(), image->getPixelRatio(), image->isSdf()));
- obsoleteImages.erase(id);
+ auto inserted = images.emplace(id, style::Image(id, image->getImage().clone(),
+ image->getPixelRatio(), image->isSdf()));
+ style.get().impl->addImage(std::make_unique<style::Image>(inserted.first->second));
}
void AnnotationManager::removeImage(const std::string& id_) {
std::lock_guard<std::mutex> lock(mutex);
const std::string id = prefixedImageID(id_);
images.erase(id);
- obsoleteImages.insert(id);
+ style.get().impl->removeImage(id);
}
double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id_) {
diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp
index 6906791db7..326565f8bc 100644
--- a/src/mbgl/annotation/annotation_manager.hpp
+++ b/src/mbgl/annotation/annotation_manager.hpp
@@ -3,8 +3,6 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/annotation/symbol_annotation_impl.hpp>
#include <mbgl/style/image.hpp>
-#include <mbgl/map/update.hpp>
-#include <mbgl/style/style.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mutex>
@@ -21,20 +19,26 @@ class AnnotationTileData;
class SymbolAnnotationImpl;
class ShapeAnnotationImpl;
+namespace style {
+class Style;
+} // namespace style
+
class AnnotationManager : private util::noncopyable {
public:
- AnnotationManager();
+ AnnotationManager(style::Style&);
~AnnotationManager();
- AnnotationID addAnnotation(const Annotation&, const uint8_t maxZoom);
- Update updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom);
+ AnnotationID addAnnotation(const Annotation&);
+ bool updateAnnotation(const AnnotationID&, const Annotation&);
void removeAnnotation(const AnnotationID&);
void addImage(std::unique_ptr<style::Image>);
void removeImage(const std::string&);
double getTopOffsetPixelsForImage(const std::string&);
- void updateStyle(style::Style::Impl&);
+ void setStyle(style::Style&);
+ void onStyleLoaded();
+
void updateData();
void addTile(AnnotationTile&);
@@ -42,24 +46,29 @@ public:
static const std::string SourceID;
static const std::string PointLayerID;
+ static const std::string ShapeLayerID;
private:
- void add(const AnnotationID&, const SymbolAnnotation&, const uint8_t);
- void add(const AnnotationID&, const LineAnnotation&, const uint8_t);
- void add(const AnnotationID&, const FillAnnotation&, const uint8_t);
-
- Update update(const AnnotationID&, const SymbolAnnotation&, const uint8_t);
- Update update(const AnnotationID&, const LineAnnotation&, const uint8_t);
- Update update(const AnnotationID&, const FillAnnotation&, const uint8_t);
+ void add(const AnnotationID&, const SymbolAnnotation&);
+ void add(const AnnotationID&, const LineAnnotation&);
+ void add(const AnnotationID&, const FillAnnotation&);
- void removeAndAdd(const AnnotationID&, const Annotation&, const uint8_t);
+ void update(const AnnotationID&, const SymbolAnnotation&);
+ void update(const AnnotationID&, const LineAnnotation&);
+ void update(const AnnotationID&, const FillAnnotation&);
void remove(const AnnotationID&);
+ void updateStyle();
+
std::unique_ptr<AnnotationTileData> getTileData(const CanonicalTileID&);
+ std::reference_wrapper<style::Style> style;
+
std::mutex mutex;
+ bool dirty = false;
+
AnnotationID nextID = 0;
using SymbolAnnotationTree = boost::geometry::index::rtree<std::shared_ptr<const SymbolAnnotationImpl>, boost::geometry::index::rstar<16, 4>>;
@@ -73,8 +82,7 @@ private:
SymbolAnnotationMap symbolAnnotations;
ShapeAnnotationMap shapeAnnotations;
ImageMap images;
- std::unordered_set<std::string> obsoleteShapeAnnotationLayers;
- std::unordered_set<std::string> obsoleteImages;
+
std::unordered_set<AnnotationTile*> tiles;
friend class AnnotationTile;
diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp
index 0596d60f4f..d405418a45 100644
--- a/src/mbgl/annotation/annotation_tile.cpp
+++ b/src/mbgl/annotation/annotation_tile.cpp
@@ -19,9 +19,6 @@ AnnotationTile::~AnnotationTile() {
annotationManager.removeTile(*this);
}
-void AnnotationTile::setNecessity(Necessity) {
-}
-
class AnnotationTileFeatureData {
public:
AnnotationTileFeatureData(const AnnotationID id_,
diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp
index 88505c50e3..a4d1e66802 100644
--- a/src/mbgl/annotation/annotation_tile.hpp
+++ b/src/mbgl/annotation/annotation_tile.hpp
@@ -14,8 +14,6 @@ public:
AnnotationTile(const OverscaledTileID&, const TileParameters&);
~AnnotationTile() override;
- void setNecessity(Necessity) final;
-
private:
AnnotationManager& annotationManager;
};
diff --git a/src/mbgl/annotation/fill_annotation_impl.cpp b/src/mbgl/annotation/fill_annotation_impl.cpp
index 5dc36edab0..9d3e12e004 100644
--- a/src/mbgl/annotation/fill_annotation_impl.cpp
+++ b/src/mbgl/annotation/fill_annotation_impl.cpp
@@ -7,9 +7,9 @@ namespace mbgl {
using namespace style;
-FillAnnotationImpl::FillAnnotationImpl(AnnotationID id_, FillAnnotation annotation_, uint8_t maxZoom_)
- : ShapeAnnotationImpl(id_, maxZoom_),
- annotation({ ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.color, annotation_.outlineColor }) {
+FillAnnotationImpl::FillAnnotationImpl(AnnotationID id_, FillAnnotation annotation_)
+ : ShapeAnnotationImpl(id_),
+ annotation(ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.color, annotation_.outlineColor) {
}
void FillAnnotationImpl::updateStyle(Style::Impl& style) const {
diff --git a/src/mbgl/annotation/fill_annotation_impl.hpp b/src/mbgl/annotation/fill_annotation_impl.hpp
index 5c49e447b8..98f9921514 100644
--- a/src/mbgl/annotation/fill_annotation_impl.hpp
+++ b/src/mbgl/annotation/fill_annotation_impl.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class FillAnnotationImpl : public ShapeAnnotationImpl {
public:
- FillAnnotationImpl(AnnotationID, FillAnnotation, uint8_t maxZoom);
+ FillAnnotationImpl(AnnotationID, FillAnnotation);
void updateStyle(style::Style::Impl&) const final;
const ShapeAnnotationGeometry& geometry() const final;
diff --git a/src/mbgl/annotation/line_annotation_impl.cpp b/src/mbgl/annotation/line_annotation_impl.cpp
index 8954ecfa58..74fec49af8 100644
--- a/src/mbgl/annotation/line_annotation_impl.cpp
+++ b/src/mbgl/annotation/line_annotation_impl.cpp
@@ -7,9 +7,9 @@ namespace mbgl {
using namespace style;
-LineAnnotationImpl::LineAnnotationImpl(AnnotationID id_, LineAnnotation annotation_, uint8_t maxZoom_)
- : ShapeAnnotationImpl(id_, maxZoom_),
- annotation({ ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.width, annotation_.color }) {
+LineAnnotationImpl::LineAnnotationImpl(AnnotationID id_, LineAnnotation annotation_)
+ : ShapeAnnotationImpl(id_),
+ annotation(ShapeAnnotationGeometry::visit(annotation_.geometry, CloseShapeAnnotation{}), annotation_.opacity, annotation_.width, annotation_.color) {
}
void LineAnnotationImpl::updateStyle(Style::Impl& style) const {
diff --git a/src/mbgl/annotation/line_annotation_impl.hpp b/src/mbgl/annotation/line_annotation_impl.hpp
index 548a094d53..108787c422 100644
--- a/src/mbgl/annotation/line_annotation_impl.hpp
+++ b/src/mbgl/annotation/line_annotation_impl.hpp
@@ -7,7 +7,7 @@ namespace mbgl {
class LineAnnotationImpl : public ShapeAnnotationImpl {
public:
- LineAnnotationImpl(AnnotationID, LineAnnotation, uint8_t maxZoom);
+ LineAnnotationImpl(AnnotationID, LineAnnotation);
void updateStyle(style::Style::Impl&) const final;
const ShapeAnnotationGeometry& geometry() const final;
diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp
index b8601d4ed2..a0b69af8d5 100644
--- a/src/mbgl/annotation/render_annotation_source.cpp
+++ b/src/mbgl/annotation/render_annotation_source.cpp
@@ -1,7 +1,7 @@
#include <mbgl/annotation/render_annotation_source.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
@@ -38,41 +38,40 @@ void RenderAnnotationSource::update(Immutable<style::Source::Impl> baseImpl_,
parameters,
SourceType::Annotations,
util::tileSize,
- { 0, 22 },
+ // Zoom level 16 is typically sufficient for annotations.
+ // See https://github.com/mapbox/mapbox-gl-native/issues/10197
+ { 0, 16 },
[&] (const OverscaledTileID& tileID) {
return std::make_unique<AnnotationTile>(tileID, parameters);
});
}
-void RenderAnnotationSource::startRender(Painter& painter) {
- painter.clipIDGenerator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(painter);
+void RenderAnnotationSource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderAnnotationSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+void RenderAnnotationSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-std::map<UnwrappedTileID, RenderTile>& RenderAnnotationSource::getRenderTiles() {
+std::vector<std::reference_wrapper<RenderTile>> RenderAnnotationSource::getRenderTiles() {
return tilePyramid.getRenderTiles();
}
std::unordered_map<std::string, std::vector<Feature>>
RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const {
return {};
}
-void RenderAnnotationSource::setCacheSize(size_t size) {
- tilePyramid.setCacheSize(size);
-}
-
void RenderAnnotationSource::onLowMemory() {
tilePyramid.onLowMemory();
}
diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp
index fe384c64ca..aa2578d4e3 100644
--- a/src/mbgl/annotation/render_annotation_source.hpp
+++ b/src/mbgl/annotation/render_annotation_source.hpp
@@ -18,21 +18,21 @@ public:
bool needsRelayout,
const TileParameters&) final;
- void startRender(Painter&) final;
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() 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 RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
@@ -44,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/annotation/shape_annotation_impl.cpp b/src/mbgl/annotation/shape_annotation_impl.cpp
index 0c1a631ad8..715dce484e 100644
--- a/src/mbgl/annotation/shape_annotation_impl.cpp
+++ b/src/mbgl/annotation/shape_annotation_impl.cpp
@@ -1,5 +1,6 @@
#include <mbgl/annotation/shape_annotation_impl.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
+#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/math/wrap.hpp>
#include <mbgl/math/clamp.hpp>
@@ -12,10 +13,9 @@ namespace mbgl {
using namespace style;
namespace geojsonvt = mapbox::geojsonvt;
-ShapeAnnotationImpl::ShapeAnnotationImpl(const AnnotationID id_, const uint8_t maxZoom_)
+ShapeAnnotationImpl::ShapeAnnotationImpl(const AnnotationID id_)
: id(id_),
- maxZoom(maxZoom_),
- layerID("com.mapbox.annotations.shape." + util::toString(id)) {
+ layerID(AnnotationManager::ShapeLayerID + util::toString(id)) {
}
void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, AnnotationTileData& data) {
@@ -27,7 +27,9 @@ void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, Annotati
return Feature { std::move(geom) };
}));
mapbox::geojsonvt::Options options;
- options.maxZoom = maxZoom;
+ // The annotation source is currently hard coded to maxzoom 16, so we're topping out at z16
+ // here as well.
+ options.maxZoom = 16;
options.buffer = 255u;
options.extent = util::EXTENT;
options.tolerance = baseTolerance;
diff --git a/src/mbgl/annotation/shape_annotation_impl.hpp b/src/mbgl/annotation/shape_annotation_impl.hpp
index ed9e8d015a..3e28221f7b 100644
--- a/src/mbgl/annotation/shape_annotation_impl.hpp
+++ b/src/mbgl/annotation/shape_annotation_impl.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
#include <mbgl/annotation/annotation.hpp>
@@ -16,7 +17,7 @@ class CanonicalTileID;
class ShapeAnnotationImpl {
public:
- ShapeAnnotationImpl(const AnnotationID, const uint8_t maxZoom);
+ ShapeAnnotationImpl(const AnnotationID);
virtual ~ShapeAnnotationImpl() = default;
virtual void updateStyle(style::Style::Impl&) const = 0;
@@ -25,7 +26,6 @@ public:
void updateTileData(const CanonicalTileID&, AnnotationTileData&);
const AnnotationID id;
- const uint8_t maxZoom;
const std::string layerID;
std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> shapeTiler;
};
diff --git a/src/mbgl/annotation/symbol_annotation_impl.hpp b/src/mbgl/annotation/symbol_annotation_impl.hpp
index 9270acb857..e41ff85f33 100644
--- a/src/mbgl/annotation/symbol_annotation_impl.hpp
+++ b/src/mbgl/annotation/symbol_annotation_impl.hpp
@@ -17,8 +17,10 @@
#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>
diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp
index b1594388c4..3b5e12b54a 100644
--- a/src/mbgl/geometry/feature_index.cpp
+++ b/src/mbgl/geometry/feature_index.cpp
@@ -1,15 +1,14 @@
#include <mbgl/geometry/feature_index.hpp>
-#include <mbgl/renderer/render_style.hpp>
#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>
-#include <mbgl/map/query.hpp>
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mapbox/geometry/envelope.hpp>
@@ -19,7 +18,7 @@
namespace mbgl {
FeatureIndex::FeatureIndex()
- : grid(util::EXTENT, 16, 0) {
+ : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) { // 16x16 grid -> 32px cell
}
void FeatureIndex::insert(const GeometryCollection& geometries,
@@ -27,24 +26,12 @@ void FeatureIndex::insert(const GeometryCollection& geometries,
const std::string& sourceLayerName,
const std::string& bucketName) {
for (const auto& ring : geometries) {
- grid.insert(IndexedSubfeature { index, sourceLayerName, bucketName, sortIndex++ },
- mapbox::geometry::envelope(ring));
+ auto envelope = mapbox::geometry::envelope(ring);
+ grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++),
+ {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)});
}
}
-static bool vectorContains(const std::vector<std::string>& vector, const std::string& s) {
- return std::find(vector.begin(), vector.end(), s) != vector.end();
-}
-
-static bool vectorsIntersect(const std::vector<std::string>& vectorA, const std::vector<std::string>& vectorB) {
- for (const auto& a : vectorA) {
- if (vectorContains(vectorB, a)) {
- return true;
- }
- }
- return false;
-}
-
static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) {
return a.sortIndex > b.sortIndex;
}
@@ -53,36 +40,6 @@ static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature&
return a.sortIndex < b.sortIndex;
}
-static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions,
- const RenderStyle& style,
- const GeometryTile& tile,
- const float pixelsToTileUnits) {
-
- // Determine the additional radius needed factoring in property functions
- float additionalRadius = 0;
- auto getQueryRadius = [&](const RenderLayer& layer) {
- auto bucket = tile.getBucket(*layer.baseImpl);
- if (bucket) {
- additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits);
- }
- };
-
- if (queryOptions.layerIDs) {
- for (const auto& layerID : *queryOptions.layerIDs) {
- const RenderLayer* layer = style.getRenderLayer(layerID);
- if (layer) {
- getQueryRadius(*layer);
- }
- }
- } else {
- for (const RenderLayer* layer : style.getRenderLayers()) {
- getQueryRadius(*layer);
- }
- }
-
- return std::min<int16_t>(util::EXTENT, additionalRadius);
-}
-
void FeatureIndex::query(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
@@ -91,18 +48,20 @@ void FeatureIndex::query(
const double scale,
const RenderedQueryOptions& queryOptions,
const GeometryTileData& geometryTileData,
- const CanonicalTileID& tileID,
- const RenderStyle& style,
- const CollisionTile* collisionTile,
- const GeometryTile& tile) const {
+ const UnwrappedTileID& tileID,
+ const std::string& sourceID,
+ const std::vector<const RenderLayer*>& layers,
+ const CollisionIndex& collisionIndex,
+ const float additionalQueryRadius) const {
// Determine query radius
const float pixelsToTileUnits = util::EXTENT / tileSize / scale;
- const int16_t additionalRadius = getAdditionalQueryRadius(queryOptions, style, tile, pixelsToTileUnits);
+ const int16_t additionalRadius = std::min<int16_t>(util::EXTENT, additionalQueryRadius * 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 - additionalRadius),
+ convertPoint<float>(box.max + additionalRadius) });
std::sort(features.begin(), features.end(), topDown);
@@ -113,18 +72,13 @@ void FeatureIndex::query(
if (indexedFeature.sortIndex == previousSortIndex) continue;
previousSortIndex = indexedFeature.sortIndex;
- addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits);
}
- // Query symbol features, if they've been placed.
- if (!collisionTile) {
- return;
- }
-
- std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale);
+ std::vector<IndexedSubfeature> symbolFeatures = collisionIndex.queryRenderedSymbols(queryGeometry, tileID, sourceID);
std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols);
for (const auto& symbolFeature : symbolFeatures) {
- addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits);
+ addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID.canonical, layers, bearing, pixelsToTileUnits);
}
}
@@ -135,30 +89,39 @@ void FeatureIndex::addFeature(
const RenderedQueryOptions& options,
const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
- const RenderStyle& style,
+ const std::vector<const RenderLayer*>& layers,
const float bearing,
const float pixelsToTileUnits) const {
- auto& layerIDs = bucketLayerIDs.at(indexedFeature.bucketName);
- if (options.layerIDs && !vectorsIntersect(layerIDs, *options.layerIDs)) {
- return;
- }
-
- auto sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName);
- assert(sourceLayer);
+ auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* {
+ for (const auto& layer : layers) {
+ if (layer->getID() == layerID) {
+ return layer;
+ }
+ }
+ return nullptr;
+ };
- auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
- assert(geometryTileFeature);
+ // Lazily calculated.
+ std::unique_ptr<GeometryTileLayer> sourceLayer;
+ std::unique_ptr<GeometryTileFeature> geometryTileFeature;
- for (const auto& layerID : layerIDs) {
- if (options.layerIDs && !vectorContains(*options.layerIDs, layerID)) {
+ for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) {
+ const RenderLayer* renderLayer = getRenderLayer(layerID);
+ if (!renderLayer) {
continue;
}
- auto renderLayer = style.getRenderLayer(layerID);
- if (!renderLayer ||
- (!renderLayer->is<RenderSymbolLayer>() &&
- !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits))) {
+ if (!geometryTileFeature) {
+ sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName);
+ assert(sourceLayer);
+
+ geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
+ assert(geometryTileFeature);
+ }
+
+ if (!renderLayer->is<RenderSymbolLayer>() &&
+ !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) {
continue;
}
diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp
index 83f339a9de..e95bb94da6 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -2,6 +2,7 @@
#include <mbgl/style/types.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/grid_index.hpp>
#include <mbgl/util/feature.hpp>
@@ -11,20 +12,40 @@
namespace mbgl {
-class GeometryTile;
class RenderedQueryOptions;
-class RenderStyle;
+class RenderLayer;
-class CollisionTile;
-class CanonicalTileID;
+class CollisionIndex;
class IndexedSubfeature {
public:
IndexedSubfeature() = delete;
- std::size_t index;
+ IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_)
+ : index(index_)
+ , sourceLayerName(std::move(sourceLayerName_))
+ , bucketName(std::move(bucketName_))
+ , sortIndex(sortIndex_)
+ , tileID(0, 0, 0)
+ {}
+
+ IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_,
+ std::string sourceID_, CanonicalTileID tileID_)
+ : index(index_)
+ , sourceLayerName(std::move(sourceLayerName_))
+ , bucketName(std::move(bucketName_))
+ , sortIndex(std::move(sortIndex_))
+ , sourceID(std::move(sourceID_))
+ , tileID(std::move(tileID_))
+ {}
+
+ size_t index;
std::string sourceLayerName;
std::string bucketName;
size_t sortIndex;
+
+ // Only used for symbol features
+ std::string sourceID;
+ CanonicalTileID tileID;
};
class FeatureIndex {
@@ -41,10 +62,11 @@ public:
const double scale,
const RenderedQueryOptions& options,
const GeometryTileData&,
- const CanonicalTileID&,
- const RenderStyle&,
- const CollisionTile*,
- const GeometryTile& tile) const;
+ const UnwrappedTileID&,
+ const std::string&,
+ const std::vector<const RenderLayer*>&,
+ const CollisionIndex&,
+ const float additionalQueryRadius) const;
static optional<GeometryCoordinates> translateQueryGeometry(
const GeometryCoordinates& queryGeometry,
@@ -63,7 +85,7 @@ private:
const RenderedQueryOptions& options,
const GeometryTileData&,
const CanonicalTileID&,
- const RenderStyle&,
+ const std::vector<const RenderLayer*>&,
const float bearing,
const float pixelsToTileUnits) const;
diff --git a/src/mbgl/gl/attribute.cpp b/src/mbgl/gl/attribute.cpp
index e05ca75866..bb5b2ddc34 100644
--- a/src/mbgl/gl/attribute.cpp
+++ b/src/mbgl/gl/attribute.cpp
@@ -1,60 +1,39 @@
#include <mbgl/gl/attribute.hpp>
-#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
namespace mbgl {
namespace gl {
-AttributeLocation bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) {
+void bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) {
+ if (location >= MAX_ATTRIBUTES) {
+ throw gl::Error("too many vertex attributes");
+ }
MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name));
- return location;
}
-void DisabledAttribute::bind(Context&, AttributeLocation location, std::size_t) const {
- MBGL_CHECK_ERROR(glDisableVertexAttribArray(location));
-}
+std::set<std::string> getActiveAttributes(ProgramID id) {
+ std::set<std::string> activeAttributes;
-template <class T> DataType DataTypeOf = static_cast<DataType>(0);
-template <> DataType DataTypeOf< int8_t> = DataType::Byte;
-template <> DataType DataTypeOf<uint8_t> = DataType::UnsignedByte;
-template <> DataType DataTypeOf< int16_t> = DataType::Short;
-template <> DataType DataTypeOf<uint16_t> = DataType::UnsignedShort;
-template <> DataType DataTypeOf< int32_t> = DataType::Integer;
-template <> DataType DataTypeOf<uint32_t> = DataType::UnsignedInteger;
-template <> DataType DataTypeOf<float> = DataType::Float;
-
-template <class T, std::size_t N>
-void AttributeBinding<T, N>::bind(Context& context, AttributeLocation location, std::size_t vertexOffset) const {
- context.vertexBuffer = vertexBuffer;
- MBGL_CHECK_ERROR(glEnableVertexAttribArray(location));
- MBGL_CHECK_ERROR(glVertexAttribPointer(
- location,
- static_cast<GLint>(attributeSize),
- static_cast<GLenum>(DataTypeOf<T>),
- static_cast<GLboolean>(false),
- static_cast<GLsizei>(vertexSize),
- reinterpret_cast<GLvoid*>(attributeOffset + (vertexSize * vertexOffset))));
-}
+ GLint attributeCount;
+ MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTES, &attributeCount));
+
+ GLint maxAttributeLength;
+ MBGL_CHECK_ERROR(glGetProgramiv(id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeLength));
+
+ std::string attributeName;
+ attributeName.resize(maxAttributeLength);
-template class AttributeBinding<uint8_t, 1>;
-template class AttributeBinding<uint8_t, 2>;
-template class AttributeBinding<uint8_t, 3>;
-template class AttributeBinding<uint8_t, 4>;
-
-template class AttributeBinding<uint16_t, 1>;
-template class AttributeBinding<uint16_t, 2>;
-template class AttributeBinding<uint16_t, 3>;
-template class AttributeBinding<uint16_t, 4>;
-
-template class AttributeBinding<int16_t, 1>;
-template class AttributeBinding<int16_t, 2>;
-template class AttributeBinding<int16_t, 3>;
-template class AttributeBinding<int16_t, 4>;
-
-template class AttributeBinding<float, 1>;
-template class AttributeBinding<float, 2>;
-template class AttributeBinding<float, 3>;
-template class AttributeBinding<float, 4>;
+ GLsizei actualLength;
+ GLint size;
+ GLenum type;
+
+ for (int32_t i = 0; i < attributeCount; i++) {
+ MBGL_CHECK_ERROR(glGetActiveAttrib(id, i, maxAttributeLength, &actualLength, &size, &type, &attributeName[0]));
+ activeAttributes.emplace(std::string(attributeName, 0, actualLength));
+ }
+
+ return activeAttributes;
+}
} // namespace gl
} // namespace mbgl
diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp
index 48222146fa..fa6c2ddeab 100644
--- a/src/mbgl/gl/attribute.hpp
+++ b/src/mbgl/gl/attribute.hpp
@@ -1,58 +1,52 @@
#pragma once
#include <mbgl/gl/types.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/util/ignore.hpp>
#include <mbgl/util/indexed_tuple.hpp>
-#include <mbgl/util/variant.hpp>
+#include <mbgl/util/optional.hpp>
#include <cstddef>
#include <vector>
+#include <set>
#include <functional>
+#include <string>
+#include <array>
+#include <limits>
namespace mbgl {
namespace gl {
-class DisabledAttribute {
-public:
- void bind(Context&, AttributeLocation, std::size_t vertexOffset) const;
+static constexpr std::size_t MAX_ATTRIBUTES = 8;
- friend bool operator==(const DisabledAttribute&,
- const DisabledAttribute&) {
- return true;
- }
-};
+template <class> struct DataTypeOf;
+template <> struct DataTypeOf< int8_t> : std::integral_constant<DataType, DataType::Byte> {};
+template <> struct DataTypeOf<uint8_t> : std::integral_constant<DataType, DataType::UnsignedByte> {};
+template <> struct DataTypeOf< int16_t> : std::integral_constant<DataType, DataType::Short> {};
+template <> struct DataTypeOf<uint16_t> : std::integral_constant<DataType, DataType::UnsignedShort> {};
+template <> struct DataTypeOf< int32_t> : std::integral_constant<DataType, DataType::Integer> {};
+template <> struct DataTypeOf<uint32_t> : std::integral_constant<DataType, DataType::UnsignedInteger> {};
+template <> struct DataTypeOf<float> : std::integral_constant<DataType, DataType::Float> {};
-template <class T, std::size_t N>
class AttributeBinding {
public:
- AttributeBinding(BufferID vertexBuffer_,
- std::size_t vertexSize_,
- std::size_t attributeOffset_,
- std::size_t attributeSize_ = N)
- : vertexBuffer(vertexBuffer_),
- vertexSize(vertexSize_),
- attributeOffset(attributeOffset_),
- attributeSize(attributeSize_)
- {}
-
- void bind(Context&, AttributeLocation, std::size_t vertexOffset) const;
+ DataType attributeType;
+ uint8_t attributeSize;
+ uint32_t attributeOffset;
+
+ BufferID vertexBuffer;
+ uint32_t vertexSize;
+ uint32_t vertexOffset;
friend bool operator==(const AttributeBinding& lhs,
const AttributeBinding& rhs) {
- return lhs.vertexBuffer == rhs.vertexBuffer
- && lhs.vertexSize == rhs.vertexSize
- && lhs.attributeOffset == rhs.attributeOffset
- && lhs.attributeSize == rhs.attributeSize;
+ return std::tie(lhs.attributeType, lhs.attributeSize, lhs.attributeOffset, lhs.vertexBuffer, lhs.vertexSize, lhs.vertexOffset)
+ == std::tie(rhs.attributeType, rhs.attributeSize, rhs.attributeOffset, rhs.vertexBuffer, rhs.vertexSize, rhs.vertexOffset);
}
-
-private:
- BufferID vertexBuffer;
- std::size_t vertexSize;
- std::size_t attributeOffset;
- std::size_t attributeSize;
};
+using AttributeBindingArray = std::array<optional<AttributeBinding>, MAX_ATTRIBUTES>;
+
/*
gl::Attribute<T,N> manages the binding of a vertex buffer to a GL program attribute.
- T is the underlying primitive type (exposed as Attribute<T,N>::ValueType)
@@ -66,10 +60,7 @@ public:
using Value = std::array<T, N>;
using Location = AttributeLocation;
-
- using Binding = variant<
- DisabledAttribute,
- AttributeBinding<T, N>>;
+ using Binding = AttributeBinding;
/*
Create a binding for this attribute. The `attributeSize` parameter may be used to
@@ -81,28 +72,29 @@ public:
std::size_t attributeIndex,
std::size_t attributeSize = N) {
static_assert(std::is_standard_layout<Vertex>::value, "vertex type must use standard layout");
- return AttributeBinding<T, N> {
+ assert(attributeSize >= 1);
+ assert(attributeSize <= 4);
+ assert(Vertex::attributeOffsets[attributeIndex] <= std::numeric_limits<uint32_t>::max());
+ static_assert(sizeof(Vertex) <= std::numeric_limits<uint32_t>::max(), "vertex too large");
+ return AttributeBinding {
+ DataTypeOf<T>::value,
+ static_cast<uint8_t>(attributeSize),
+ static_cast<uint32_t>(Vertex::attributeOffsets[attributeIndex]),
buffer.buffer,
- sizeof(Vertex),
- Vertex::attributeOffsets[attributeIndex],
- attributeSize
+ static_cast<uint32_t>(sizeof(Vertex)),
+ 0,
};
}
- static void bind(Context& context,
- const Location& location,
- Binding& oldBinding,
- const Binding& newBinding,
- std::size_t vertexOffset) {
- if (oldBinding == newBinding) {
- return;
+ static optional<Binding> offsetBinding(const optional<Binding>& binding, std::size_t vertexOffset) {
+ assert(vertexOffset <= std::numeric_limits<uint32_t>::max());
+ if (binding) {
+ AttributeBinding result = *binding;
+ result.vertexOffset = static_cast<uint32_t>(vertexOffset);
+ return result;
+ } else {
+ return binding;
}
-
- Binding::visit(newBinding, [&] (const auto& binding) {
- binding.bind(context, location, vertexOffset);
- });
-
- oldBinding = newBinding;
}
};
@@ -222,7 +214,8 @@ const std::size_t Vertex<A1, A2, A3, A4, A5>::attributeOffsets[5] = {
} // namespace detail
-AttributeLocation bindAttributeLocation(ProgramID, AttributeLocation, const char * name);
+void bindAttributeLocation(ProgramID, AttributeLocation, const char * name);
+std::set<std::string> getActiveAttributes(ProgramID);
template <class... As>
class Attributes {
@@ -230,19 +223,28 @@ public:
using Types = TypeList<As...>;
using Locations = IndexedTuple<
TypeList<As...>,
- TypeList<typename As::Type::Location...>>;
+ TypeList<optional<typename As::Type::Location>...>>;
using Bindings = IndexedTuple<
TypeList<As...>,
- TypeList<typename As::Type::Binding...>>;
+ TypeList<optional<typename As::Type::Binding>...>>;
using NamedLocations = std::vector<std::pair<const std::string, AttributeLocation>>;
using Vertex = detail::Vertex<typename As::Type...>;
- template <class A>
- static constexpr std::size_t Index = TypeIndex<A, As...>::value;
-
static Locations bindLocations(const ProgramID& id) {
- return Locations { bindAttributeLocation(id, Index<As>, As::name())... };
+ std::set<std::string> activeAttributes = getActiveAttributes(id);
+
+ AttributeLocation location = 0;
+ auto maybeBindLocation = [&](const char* name) -> optional<AttributeLocation> {
+ if (activeAttributes.count(name)) {
+ bindAttributeLocation(id, location, name);
+ return location++;
+ } else {
+ return {};
+ }
+ };
+
+ return Locations { maybeBindLocation(As::name())... };
}
template <class Program>
@@ -251,24 +253,41 @@ public:
}
static NamedLocations getNamedLocations(const Locations& locations) {
- return NamedLocations{ { As::name(), locations.template get<As>() }... };
+ NamedLocations result;
+
+ auto maybeAddLocation = [&] (const std::string& name, const optional<AttributeLocation>& location) {
+ if (location) {
+ result.emplace_back(name, *location);
+ }
+ };
+
+ util::ignore({ (maybeAddLocation(As::name(), locations.template get<As>()), 0)... });
+
+ return result;
}
template <class DrawMode>
static Bindings bindings(const VertexBuffer<Vertex, DrawMode>& buffer) {
- return Bindings { As::Type::binding(buffer, Index<As>)... };
+ return Bindings { As::Type::binding(buffer, TypeIndex<As, As...>::value)... };
}
- static void bind(Context& context,
- const Locations& locations,
- Bindings& oldBindings,
- const Bindings& newBindings,
- std::size_t vertexOffset) {
- util::ignore({ (As::Type::bind(context,
- locations.template get<As>(),
- oldBindings.template get<As>(),
- newBindings.template get<As>(),
- vertexOffset), 0)... });
+ static Bindings offsetBindings(const Bindings& bindings, std::size_t vertexOffset) {
+ return Bindings { As::Type::offsetBinding(bindings.template get<As>(), vertexOffset)... };
+ }
+
+ static AttributeBindingArray toBindingArray(const Locations& locations, const Bindings& bindings) {
+ AttributeBindingArray result;
+
+ auto maybeAddBinding = [&] (const optional<AttributeLocation>& location,
+ const optional<AttributeBinding>& binding) {
+ if (location) {
+ result.at(*location) = binding;
+ }
+ };
+
+ util::ignore({ (maybeAddBinding(locations.template get<As>(), bindings.template get<As>()), 0)... });
+
+ return result;
}
};
diff --git a/src/mbgl/gl/color_mode.hpp b/src/mbgl/gl/color_mode.hpp
index e73c8737eb..c6594a3a77 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>;
diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp
index 1b4d6fbcb7..cc5aa014ed 100644
--- a/src/mbgl/gl/context.cpp
+++ b/src/mbgl/gl/context.cpp
@@ -1,4 +1,3 @@
-#include <mbgl/map/view.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/debugging_extension.hpp>
@@ -16,6 +15,31 @@ namespace gl {
static_assert(underlying_type(ShaderType::Vertex) == GL_VERTEX_SHADER, "OpenGL type mismatch");
static_assert(underlying_type(ShaderType::Fragment) == GL_FRAGMENT_SHADER, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::Byte) == GL_BYTE, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::UnsignedByte) == GL_UNSIGNED_BYTE, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::Short) == GL_SHORT, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::UnsignedShort) == GL_UNSIGNED_SHORT, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::Integer) == GL_INT, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::UnsignedInteger) == GL_UNSIGNED_INT, "OpenGL type mismatch");
+static_assert(underlying_type(DataType::Float) == GL_FLOAT, "OpenGL type mismatch");
+
+#if not MBGL_USE_GLES2
+static_assert(underlying_type(RenderbufferType::RGBA) == GL_RGBA8, "OpenGL type mismatch");
+#else
+static_assert(underlying_type(RenderbufferType::RGBA) == GL_RGBA8_OES, "OpenGL type mismatch");
+#endif // MBGL_USE_GLES2
+#if not MBGL_USE_GLES2
+static_assert(underlying_type(RenderbufferType::DepthStencil) == GL_DEPTH24_STENCIL8, "OpenGL type mismatch");
+#else
+static_assert(underlying_type(RenderbufferType::DepthStencil) == GL_DEPTH24_STENCIL8_OES, "OpenGL type mismatch");
+#endif // MBGL_USE_GLES2
+#if not MBGL_USE_GLES2
+static_assert(underlying_type(RenderbufferType::DepthComponent) == GL_DEPTH_COMPONENT, "OpenGL type mismatch");
+#else
+static_assert(underlying_type(RenderbufferType::DepthComponent) == GL_DEPTH_COMPONENT16, "OpenGL type mismatch");
+#endif // MBGL_USE_GLES2
+
+
static_assert(underlying_type(PrimitiveType::Points) == GL_POINTS, "OpenGL type mismatch");
static_assert(underlying_type(PrimitiveType::Lines) == GL_LINES, "OpenGL type mismatch");
static_assert(underlying_type(PrimitiveType::LineLoop) == GL_LINE_LOOP, "OpenGL type mismatch");
@@ -36,12 +60,36 @@ 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(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");
+static_assert(underlying_type(UniformDataType::FloatVec4) == GL_FLOAT_VEC4, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::Int) == GL_INT, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::IntVec2) == GL_INT_VEC2, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::IntVec3) == GL_INT_VEC3, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::IntVec4) == GL_INT_VEC4, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::Bool) == GL_BOOL, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::BoolVec2) == GL_BOOL_VEC2, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::BoolVec3) == GL_BOOL_VEC3, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::BoolVec4) == GL_BOOL_VEC4, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatMat2) == GL_FLOAT_MAT2, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatMat3) == GL_FLOAT_MAT3, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::FloatMat4) == GL_FLOAT_MAT4, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::Sampler2D) == GL_SAMPLER_2D, "OpenGL type mismatch");
+static_assert(underlying_type(UniformDataType::SamplerCube) == GL_SAMPLER_CUBE, "OpenGL type mismatch");
+
+static_assert(underlying_type(BufferUsage::StreamDraw) == GL_STREAM_DRAW, "OpenGL type mismatch");
+static_assert(underlying_type(BufferUsage::StaticDraw) == GL_STATIC_DRAW, "OpenGL type mismatch");
+static_assert(underlying_type(BufferUsage::DynamicDraw) == GL_DYNAMIC_DRAW, "OpenGL type mismatch");
+
static_assert(std::is_same<BinaryProgramFormat, GLenum>::value, "OpenGL type mismatch");
Context::Context() = default;
Context::~Context() {
- reset();
+ if (cleanupOnDestruction) {
+ reset();
+ }
}
void Context::initializeExtensions(const std::function<gl::ProcAddress(const char*)>& getProcAddress) {
@@ -164,25 +212,39 @@ void Context::verifyProgramLinkage(ProgramID program_) {
throw std::runtime_error("program failed to link");
}
-UniqueBuffer Context::createVertexBuffer(const void* data, std::size_t size) {
+UniqueBuffer Context::createVertexBuffer(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 } };
vertexBuffer = result;
- MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
+ MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, size, data, static_cast<GLenum>(usage)));
return result;
}
-UniqueBuffer Context::createIndexBuffer(const void* data, std::size_t size) {
+void Context::updateVertexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size) {
+ vertexBuffer = buffer;
+ MBGL_CHECK_ERROR(glBufferSubData(GL_ARRAY_BUFFER, 0, size, data));
+}
+
+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 } };
- vertexArrayObject = 0;
- elementBuffer = result;
- MBGL_CHECK_ERROR(glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
+ bindVertexArray = 0;
+ globalVertexArrayState.indexBuffer = result;
+ 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);
@@ -195,7 +257,14 @@ UniqueTexture Context::createTexture() {
}
bool Context::supportsVertexArrays() const {
- return vertexArray &&
+ static bool blacklisted = []() {
+ // Blacklist Adreno 3xx as it crashes on glBuffer(Sub)Data
+ const std::string renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
+ return renderer.find("Adreno (TM) 3") != std::string::npos;
+ }();
+
+ return !blacklisted &&
+ vertexArray &&
vertexArray->genVertexArrays &&
vertexArray->bindVertexArray &&
vertexArray->deleteVertexArrays;
@@ -203,7 +272,21 @@ bool Context::supportsVertexArrays() const {
#if MBGL_HAS_BINARY_PROGRAMS
bool Context::supportsProgramBinaries() const {
- return programBinary && programBinary->programBinary && programBinary->getProgramBinary;
+ if (!programBinary || !programBinary->programBinary || !programBinary->getProgramBinary) {
+ return false;
+ }
+
+ // Blacklist Adreno 3xx, 4xx, and 5xx GPUs due to known bugs:
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=510637
+ // https://chromium.googlesource.com/chromium/src/gpu/+/master/config/gpu_driver_bug_list.json#2316
+ const std::string renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
+ if (renderer.find("Adreno (TM) 3") != std::string::npos
+ || renderer.find("Adreno (TM) 4") != std::string::npos
+ || renderer.find("Adreno (TM) 5") != std::string::npos) {
+ return false;
+ }
+
+ return true;
}
optional<std::pair<BinaryProgramFormat, std::string>>
@@ -229,11 +312,17 @@ optional<std::pair<BinaryProgramFormat, std::string>> Context::getBinaryProgram(
}
#endif
-UniqueVertexArray Context::createVertexArray() {
- assert(supportsVertexArrays());
- VertexArrayID id = 0;
- MBGL_CHECK_ERROR(vertexArray->genVertexArrays(1, &id));
- return UniqueVertexArray(std::move(id), { this });
+VertexArray Context::createVertexArray() {
+ if (supportsVertexArrays()) {
+ VertexArrayID id = 0;
+ MBGL_CHECK_ERROR(vertexArray->genVertexArrays(1, &id));
+ UniqueVertexArray vao(std::move(id), { this });
+ return { UniqueVertexArrayState(new VertexArrayState(std::move(vao), *this), VertexArrayStateDeleter { true })};
+ } else {
+ // On GL implementations which do not support vertex arrays, attribute bindings are global state.
+ // So return a VertexArray which shares our global state tracking and whose deleter is a no-op.
+ return { UniqueVertexArrayState(&globalVertexArrayState, VertexArrayStateDeleter { false }) };
+ }
}
UniqueFramebuffer Context::createFramebuffer() {
@@ -386,6 +475,9 @@ Framebuffer Context::createFramebuffer(const Texture& color) {
Framebuffer
Context::createFramebuffer(const Texture& color,
const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) {
+ if (color.size != depthTarget.size) {
+ throw std::runtime_error("Renderbuffer size mismatch");
+ }
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0));
@@ -410,7 +502,7 @@ 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) {
- activeTexture = unit;
+ 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,
@@ -424,7 +516,7 @@ void Context::bindTexture(Texture& obj,
TextureWrap wrapX,
TextureWrap wrapY) {
if (filter != obj.filter || mipmap != obj.mipmap || wrapX != obj.wrapX || wrapY != obj.wrapY) {
- activeTexture = unit;
+ activeTextureUnit = unit;
texture[unit] = obj.texture;
if (filter != obj.filter || mipmap != obj.mipmap) {
@@ -455,7 +547,7 @@ void Context::bindTexture(Texture& obj,
} else if (texture[unit] != obj.texture) {
// We are checking first to avoid setting the active texture without a subsequent
// texture bind.
- activeTexture = unit;
+ activeTextureUnit = unit;
texture[unit] = obj.texture;
}
}
@@ -487,7 +579,7 @@ void Context::setDirtyState() {
clearStencil.setDirty();
program.setDirty();
lineWidth.setDirty();
- activeTexture.setDirty();
+ activeTextureUnit.setDirty();
pixelStorePack.setDirty();
pixelStoreUnpack.setDirty();
#if not MBGL_USE_GLES2
@@ -501,8 +593,8 @@ void Context::setDirtyState() {
tex.setDirty();
}
vertexBuffer.setDirty();
- elementBuffer.setDirty();
- vertexArrayObject.setDirty();
+ bindVertexArray.setDirty();
+ globalVertexArrayState.setDirty();
}
void Context::clear(optional<mbgl::Color> color,
@@ -513,19 +605,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));
@@ -557,6 +649,13 @@ void Context::setDrawMode(const TriangleStrip&) {
void Context::setDepthMode(const DepthMode& depth) {
if (depth.func == DepthMode::Always && !depth.mask) {
depthTest = false;
+
+ // Workaround for rendering errors on Adreno 2xx GPUs. Depth-related state should
+ // not matter when the depth test is disabled, but on these GPUs it apparently does.
+ // https://github.com/mapbox/mapbox-gl-native/issues/9164
+ depthFunc = depth.func;
+ depthMask = depth.mask;
+ depthRange = depth.range;
} else {
depthTest = true;
depthFunc = depth.func;
@@ -621,8 +720,8 @@ void Context::performCleanup() {
for (const auto id : abandonedBuffers) {
if (vertexBuffer == id) {
vertexBuffer.setDirty();
- } else if (elementBuffer == id) {
- elementBuffer.setDirty();
+ } else if (globalVertexArrayState.indexBuffer == id) {
+ globalVertexArrayState.indexBuffer.setDirty();
}
}
MBGL_CHECK_ERROR(glDeleteBuffers(int(abandonedBuffers.size()), abandonedBuffers.data()));
@@ -631,8 +730,10 @@ void Context::performCleanup() {
if (!abandonedTextures.empty()) {
for (const auto id : abandonedTextures) {
- if (activeTexture == id) {
- activeTexture.setDirty();
+ for (auto& binding : texture) {
+ if (binding == id) {
+ binding.setDirty();
+ }
}
}
MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data()));
@@ -642,8 +743,8 @@ void Context::performCleanup() {
if (!abandonedVertexArrays.empty()) {
assert(supportsVertexArrays());
for (const auto id : abandonedVertexArrays) {
- if (vertexArrayObject == id) {
- vertexArrayObject.setDirty();
+ if (bindVertexArray == id) {
+ bindVertexArray.setDirty();
}
}
MBGL_CHECK_ERROR(vertexArray->deleteVertexArrays(int(abandonedVertexArrays.size()),
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index 4f5b4c797c..14f078367f 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -9,6 +9,7 @@
#include <mbgl/gl/framebuffer.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/gl/vertex_array.hpp>
#include <mbgl/gl/types.hpp>
#include <mbgl/gl/draw_mode.hpp>
#include <mbgl/gl/depth_mode.hpp>
@@ -24,9 +25,6 @@
#include <string>
namespace mbgl {
-
-class View;
-
namespace gl {
constexpr size_t TextureMax = 64;
@@ -53,9 +51,7 @@ public:
void verifyProgramLinkage(ProgramID);
void linkProgram(ProgramID);
UniqueTexture createTexture();
-
- bool supportsVertexArrays() const;
- UniqueVertexArray createVertexArray();
+ VertexArray createVertexArray();
#if MBGL_HAS_BINARY_PROGRAMS
bool supportsProgramBinaries() const;
@@ -65,19 +61,32 @@ public:
optional<std::pair<BinaryProgramFormat, std::string>> getBinaryProgram(ProgramID) const;
template <class Vertex, class DrawMode>
- VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v) {
+ VertexBuffer<Vertex, DrawMode> createVertexBuffer(VertexVector<Vertex, DrawMode>&& v, const BufferUsage usage = BufferUsage::StaticDraw) {
return VertexBuffer<Vertex, DrawMode> {
v.vertexSize(),
- createVertexBuffer(v.data(), v.byteSize())
+ createVertexBuffer(v.data(), v.byteSize(), usage)
};
}
+ template <class Vertex, class DrawMode>
+ void updateVertexBuffer(VertexBuffer<Vertex, DrawMode>& buffer, VertexVector<Vertex, DrawMode>&& v) {
+ assert(v.vertexSize() == buffer.vertexCount);
+ updateVertexBuffer(buffer.buffer, v.data(), v.byteSize());
+ }
+
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) {
@@ -188,7 +197,13 @@ public:
return vertexArray.get();
}
+ void setCleanupOnDestruction(bool cleanup) {
+ cleanupOnDestruction = cleanup;
+ }
+
private:
+ bool cleanupOnDestruction = true;
+
std::unique_ptr<extension::Debugging> debugging;
std::unique_ptr<extension::VertexArray> vertexArray;
#if MBGL_HAS_BINARY_PROGRAMS
@@ -196,15 +211,16 @@ private:
#endif
public:
- State<value::ActiveTexture> activeTexture;
+ State<value::ActiveTextureUnit> activeTextureUnit;
State<value::BindFramebuffer> bindFramebuffer;
State<value::Viewport> viewport;
State<value::ScissorTest> scissorTest;
std::array<State<value::BindTexture>, 2> texture;
- State<value::BindVertexArray, const Context&> vertexArrayObject { *this };
State<value::Program> program;
State<value::BindVertexBuffer> vertexBuffer;
- State<value::BindElementBuffer> elementBuffer;
+
+ State<value::BindVertexArray, const Context&> bindVertexArray { *this };
+ VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }), *this };
State<value::PixelStorePack> pixelStorePack;
State<value::PixelStoreUnpack> pixelStoreUnpack;
@@ -239,8 +255,10 @@ private:
State<value::PointSize> pointSize;
#endif // MBGL_USE_GLES2
- UniqueBuffer createVertexBuffer(const void* data, std::size_t size);
- UniqueBuffer createIndexBuffer(const void* data, std::size_t size);
+ 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, const BufferUsage usage);
+ void updateIndexBuffer(UniqueBuffer& buffer, const void* data, std::size_t size);
UniqueTexture createTexture(Size size, const void* data, TextureFormat, TextureUnit);
void updateTexture(TextureID, Size size, const void* data, TextureFormat, TextureUnit);
UniqueFramebuffer createFramebuffer();
@@ -250,6 +268,8 @@ private:
void drawPixels(Size size, const void* data, TextureFormat);
#endif // MBGL_USE_GLES2
+ bool supportsVertexArrays() const;
+
friend detail::ProgramDeleter;
friend detail::ShaderDeleter;
friend detail::BufferDeleter;
@@ -269,8 +289,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.cpp b/src/mbgl/gl/gl.cpp
index 8999468dbd..bd6d7b192d 100644
--- a/src/mbgl/gl/gl.cpp
+++ b/src/mbgl/gl/gl.cpp
@@ -1,12 +1,13 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/util/string.hpp>
+#include <mbgl/util/util.hpp>
namespace mbgl {
namespace gl {
namespace {
-constexpr const char* stringFromError(GLenum err) {
+MBGL_CONSTEXPR const char* stringFromError(GLenum err) {
switch (err) {
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
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 1e57fb11f2..87bfb6068f 100644
--- a/src/mbgl/gl/index_buffer.hpp
+++ b/src/mbgl/gl/index_buffer.hpp
@@ -26,6 +26,7 @@ public:
bool empty() const { return v.empty(); }
void clear() { v.clear(); }
const uint16_t* data() const { return v.data(); }
+ const std::vector<uint16_t>& vector() const { return v; }
private:
std::vector<uint16_t> v;
@@ -34,6 +35,7 @@ private:
template <class DrawMode>
class IndexBuffer {
public:
+ std::size_t indexCount;
UniqueBuffer buffer;
};
diff --git a/src/mbgl/gl/object.cpp b/src/mbgl/gl/object.cpp
index e2d476e0c0..2c5f1bca1f 100644
--- a/src/mbgl/gl/object.cpp
+++ b/src/mbgl/gl/object.cpp
@@ -33,7 +33,9 @@ void TextureDeleter::operator()(TextureID id) const {
void VertexArrayDeleter::operator()(VertexArrayID id) const {
assert(context);
- context->abandonedVertexArrays.push_back(id);
+ if (id != 0) {
+ context->abandonedVertexArrays.push_back(id);
+ }
}
void FramebufferDeleter::operator()(FramebufferID id) const {
diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp
index 47ad39de7c..3b54ec194a 100644
--- a/src/mbgl/gl/program.hpp
+++ b/src/mbgl/gl/program.hpp
@@ -5,15 +5,16 @@
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/gl/vertex_array.hpp>
#include <mbgl/gl/attribute.hpp>
#include <mbgl/gl/uniform.hpp>
#include <mbgl/util/io.hpp>
+#include <mbgl/util/logging.hpp>
#include <mbgl/programs/binary_program.hpp>
#include <mbgl/programs/program_parameters.hpp>
#include <mbgl/shaders/shaders.hpp>
-
#include <string>
namespace mbgl {
@@ -33,15 +34,17 @@ public:
: program(
context.createProgram(context.createShader(ShaderType::Vertex, vertexSource),
context.createShader(ShaderType::Fragment, fragmentSource))),
- attributeLocations(Attributes::bindLocations(program)),
- uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))) {
+ uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))),
+ attributeLocations(Attributes::bindLocations(program)) {
+ // Re-link program after manually binding only active attributes in Attributes::bindLocations
+ context.linkProgram(program);
}
template <class BinaryProgram>
Program(Context& context, const BinaryProgram& binaryProgram)
: program(context.createProgram(binaryProgram.format(), binaryProgram.code())),
- attributeLocations(Attributes::loadNamedLocations(binaryProgram)),
- uniformsState(Uniforms::loadNamedLocations(binaryProgram)) {
+ uniformsState(Uniforms::loadNamedLocations(binaryProgram)),
+ attributeLocations(Attributes::loadNamedLocations(binaryProgram)) {
}
static Program createProgram(gl::Context& context,
@@ -49,15 +52,13 @@ public:
const char* name,
const char* vertexSource_,
const char* fragmentSource_) {
+ const std::string vertexSource = shaders::vertexSource(programParameters, vertexSource_);
+ const std::string fragmentSource = shaders::fragmentSource(programParameters, fragmentSource_);
+
#if MBGL_HAS_BINARY_PROGRAMS
optional<std::string> cachePath = programParameters.cachePath(name);
if (cachePath && context.supportsProgramBinaries()) {
- const std::string vertexSource =
- shaders::vertexSource(programParameters, vertexSource_);
- const std::string fragmentSource =
- shaders::fragmentSource(programParameters, fragmentSource_);
- const std::string identifier =
- shaders::programIdentifier(vertexSource, fragmentSource_);
+ const std::string identifier = shaders::programIdentifier(vertexSource, fragmentSource);
try {
if (auto cachedBinaryProgram = util::readFile(*cachePath)) {
@@ -91,11 +92,9 @@ public:
return std::move(result);
}
#endif
+
(void)name;
- return Program {
- context, shaders::vertexSource(programParameters, vertexSource_),
- shaders::fragmentSource(programParameters, fragmentSource_)
- };
+ return Program { context, vertexSource, fragmentSource };
}
template <class BinaryProgram>
@@ -114,10 +113,12 @@ public:
DepthMode depthMode,
StencilMode stencilMode,
ColorMode colorMode,
- UniformValues&& uniformValues,
- AttributeBindings&& attributeBindings,
+ const UniformValues& uniformValues,
+ VertexArray& vertexArray,
+ const AttributeBindings& attributeBindings,
const IndexBuffer<DrawMode>& indexBuffer,
- const SegmentVector<Attributes>& segments) {
+ std::size_t indexOffset,
+ std::size_t indexLength) {
static_assert(std::is_same<Primitive, typename DrawMode::Primitive>::value, "incompatible draw mode");
context.setDrawMode(drawMode);
@@ -127,25 +128,22 @@ public:
context.program = program;
- Uniforms::bind(uniformsState, std::move(uniformValues));
+ Uniforms::bind(uniformsState, uniformValues);
- for (const auto& segment : segments) {
- segment.bind(context,
- indexBuffer.buffer,
- attributeLocations,
- attributeBindings);
+ vertexArray.bind(context,
+ indexBuffer.buffer,
+ Attributes::toBindingArray(attributeLocations, attributeBindings));
- context.draw(drawMode.primitiveType,
- segment.indexOffset,
- segment.indexLength);
- }
+ context.draw(drawMode.primitiveType,
+ indexOffset,
+ indexLength);
}
private:
UniqueProgram program;
- typename Attributes::Locations attributeLocations;
typename Uniforms::State uniformsState;
+ typename Attributes::Locations attributeLocations;
};
} // namespace gl
diff --git a/src/mbgl/gl/renderbuffer.hpp b/src/mbgl/gl/renderbuffer.hpp
index cc8ff13268..0592557a7f 100644
--- a/src/mbgl/gl/renderbuffer.hpp
+++ b/src/mbgl/gl/renderbuffer.hpp
@@ -9,9 +9,23 @@ namespace gl {
template <RenderbufferType renderbufferType>
class Renderbuffer {
public:
+ Renderbuffer(Size size_, UniqueRenderbuffer renderbuffer_, bool dirty_ = false)
+ : size(std::move(size_)), renderbuffer(std::move(renderbuffer_)), dirty(dirty_) {
+ }
+
using type = std::integral_constant<RenderbufferType, renderbufferType>;
Size size;
- gl::UniqueRenderbuffer renderbuffer;
+ UniqueRenderbuffer renderbuffer;
+
+ void shouldClear(bool clear) {
+ dirty = clear;
+ }
+ bool needsClearing() {
+ return dirty;
+ }
+
+private:
+ bool dirty;
};
} // namespace gl
diff --git a/src/mbgl/gl/segment.cpp b/src/mbgl/gl/segment.cpp
deleted file mode 100644
index aabdc83cd4..0000000000
--- a/src/mbgl/gl/segment.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include <mbgl/gl/segment.hpp>
-
-namespace mbgl {
-namespace gl {
-
-} // namespace gl
-} // namespace mbgl
diff --git a/src/mbgl/gl/segment.hpp b/src/mbgl/gl/segment.hpp
deleted file mode 100644
index fe0658bf8e..0000000000
--- a/src/mbgl/gl/segment.hpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#pragma once
-
-#include <mbgl/gl/context.hpp>
-#include <mbgl/gl/vertex_buffer.hpp>
-#include <mbgl/util/optional.hpp>
-#include <mbgl/util/logging.hpp>
-
-#include <cstddef>
-#include <vector>
-
-namespace mbgl {
-namespace gl {
-
-template <class Attributes>
-class Segment {
-public:
- Segment(std::size_t vertexOffset_,
- std::size_t indexOffset_,
- std::size_t vertexLength_ = 0,
- std::size_t indexLength_ = 0)
- : vertexOffset(vertexOffset_),
- indexOffset(indexOffset_),
- vertexLength(vertexLength_),
- indexLength(indexLength_) {}
-
- const std::size_t vertexOffset;
- const std::size_t indexOffset;
-
- std::size_t vertexLength;
- std::size_t indexLength;
-
- void bind(Context& context,
- BufferID indexBuffer_,
- const typename Attributes::Locations& attributeLocations,
- const typename Attributes::Bindings& attributeBindings_) const {
- if (context.supportsVertexArrays()) {
- if (!vao) {
- vao = context.createVertexArray();
- context.vertexBuffer.setDirty();
- }
- context.vertexArrayObject = *vao;
- if (indexBuffer != indexBuffer_) {
- indexBuffer = indexBuffer_;
- context.elementBuffer.setDirty();
- context.elementBuffer = indexBuffer_;
- }
- } else {
- // No VAO support. Force attributes to be rebound.
- context.elementBuffer = indexBuffer_;
- attributeBindings = {};
- }
-
- Attributes::bind(context,
- attributeLocations,
- attributeBindings,
- attributeBindings_,
- vertexOffset);
- }
-
-private:
- mutable optional<UniqueVertexArray> vao;
- mutable optional<BufferID> indexBuffer;
- mutable typename Attributes::Bindings attributeBindings;
-};
-
-template <class Attributes>
-class SegmentVector : public std::vector<Segment<Attributes>> {
-public:
- SegmentVector() = default;
-};
-
-} // namespace gl
-} // namespace mbgl
diff --git a/src/mbgl/gl/texture.hpp b/src/mbgl/gl/texture.hpp
index 5330689ac2..625e69233a 100644
--- a/src/mbgl/gl/texture.hpp
+++ b/src/mbgl/gl/texture.hpp
@@ -8,12 +8,24 @@ namespace gl {
class Texture {
public:
+ Texture(Size size_, UniqueTexture texture_,
+ TextureFilter filter_ = TextureFilter::Nearest,
+ TextureMipMap mipmap_ = TextureMipMap::No,
+ TextureWrap wrapX_ = TextureWrap::Clamp,
+ TextureWrap wrapY_ = TextureWrap::Clamp)
+ : size(std::move(size_)),
+ texture(std::move(texture_)),
+ filter(filter_),
+ mipmap(mipmap_),
+ wrapX(wrapX_),
+ wrapY(wrapY_) {}
+
Size size;
UniqueTexture texture;
- TextureFilter filter = TextureFilter::Nearest;
- TextureMipMap mipmap = TextureMipMap::No;
- TextureWrap wrapX = TextureWrap::Clamp;
- TextureWrap wrapY = TextureWrap::Clamp;
+ TextureFilter filter;
+ TextureMipMap mipmap;
+ TextureWrap wrapX;
+ TextureWrap wrapY;
};
} // namespace gl
diff --git a/src/mbgl/gl/types.hpp b/src/mbgl/gl/types.hpp
index 74ce67fba6..da08195e58 100644
--- a/src/mbgl/gl/types.hpp
+++ b/src/mbgl/gl/types.hpp
@@ -15,8 +15,16 @@ using VertexArrayID = uint32_t;
using FramebufferID = uint32_t;
using RenderbufferID = uint32_t;
-using AttributeLocation = int32_t;
+// OpenGL does not formally define a type for attribute locations, but most APIs use
+// GLuint. The exception is glGetAttribLocation, which returns GLint so that -1 can
+// be used as an error indicator.
+using AttributeLocation = uint32_t;
+
+// OpenGL does not formally define a type for uniform locations, but all APIs use GLint.
+// The value -1 is special, typically used as a placeholder for an unused uniform and
+// "silently ignored".
using UniformLocation = int32_t;
+
using TextureUnit = uint8_t;
enum class ShaderType : uint32_t {
@@ -24,7 +32,7 @@ enum class ShaderType : uint32_t {
Fragment = 0x8B30
};
-enum class DataType : uint32_t {
+enum class DataType : uint16_t {
Byte = 0x1400,
UnsignedByte = 0x1401,
Short = 0x1402,
@@ -96,5 +104,11 @@ enum class UniformDataType : uint32_t {
SamplerCube = 0x8B60,
};
+enum class BufferUsage : uint32_t {
+ StreamDraw = 0x88E0,
+ StaticDraw = 0x88E4,
+ DynamicDraw = 0x88E8,
+};
+
} // namespace gl
} // namespace mbgl
diff --git a/src/mbgl/gl/uniform.hpp b/src/mbgl/gl/uniform.hpp
index 829192bcca..5a78068fc8 100644
--- a/src/mbgl/gl/uniform.hpp
+++ b/src/mbgl/gl/uniform.hpp
@@ -48,6 +48,8 @@ public:
class State {
public:
+ State(UniformLocation location_) : location(std::move(location_)) {}
+
void operator=(const Value& value) {
if (location >= 0 && (!current || *current != value.t)) {
current = value.t;
@@ -106,14 +108,14 @@ public:
template <class Program>
static State loadNamedLocations(const Program& program) {
- return State{ { program.uniformLocation(Us::name()) }... };
+ return State(typename Us::State(program.uniformLocation(Us::name()))...);
}
static NamedLocations getNamedLocations(const State& state) {
return NamedLocations{ { Us::name(), state.template get<Us>().location }... };
}
- static void bind(State& state, Values&& values) {
+ static void bind(State& state, const Values& values) {
util::ignore({ (state.template get<Us>() = values.template get<Us>(), 0)... });
}
};
diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp
index 2b825fb2bd..092403af0d 100644
--- a/src/mbgl/gl/value.cpp
+++ b/src/mbgl/gl/value.cpp
@@ -242,13 +242,13 @@ LineWidth::Type LineWidth::Get() {
return lineWidth;
}
-const constexpr ActiveTexture::Type ActiveTexture::Default;
+const constexpr ActiveTextureUnit::Type ActiveTextureUnit::Default;
-void ActiveTexture::Set(const Type& value) {
+void ActiveTextureUnit::Set(const Type& value) {
MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value));
}
-ActiveTexture::Type ActiveTexture::Get() {
+ActiveTextureUnit::Type ActiveTextureUnit::Get() {
GLint activeTexture;
MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture));
return static_cast<Type>(activeTexture - GL_TEXTURE0);
@@ -365,6 +365,24 @@ BindVertexArray::Type BindVertexArray::Get(const Context& context) {
return binding;
}
+const optional<AttributeBinding> VertexAttribute::Default {};
+
+void VertexAttribute::Set(const optional<AttributeBinding>& binding, Context& context, AttributeLocation location) {
+ if (binding) {
+ context.vertexBuffer = binding->vertexBuffer;
+ MBGL_CHECK_ERROR(glEnableVertexAttribArray(location));
+ MBGL_CHECK_ERROR(glVertexAttribPointer(
+ location,
+ static_cast<GLint>(binding->attributeSize),
+ static_cast<GLenum>(binding->attributeType),
+ static_cast<GLboolean>(false),
+ static_cast<GLsizei>(binding->vertexSize),
+ reinterpret_cast<GLvoid*>(binding->attributeOffset + (binding->vertexSize * binding->vertexOffset))));
+ } else {
+ MBGL_CHECK_ERROR(glDisableVertexAttribArray(location));
+ }
+}
+
const constexpr PixelStorePack::Type PixelStorePack::Default;
void PixelStorePack::Set(const Type& value) {
diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp
index d4c7b5cdc3..7b85a5ff4b 100644
--- a/src/mbgl/gl/value.hpp
+++ b/src/mbgl/gl/value.hpp
@@ -4,6 +4,7 @@
#include <mbgl/gl/depth_mode.hpp>
#include <mbgl/gl/stencil_mode.hpp>
#include <mbgl/gl/color_mode.hpp>
+#include <mbgl/gl/attribute.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/size.hpp>
#include <mbgl/util/range.hpp>
@@ -164,7 +165,7 @@ struct LineWidth {
static Type Get();
};
-struct ActiveTexture {
+struct ActiveTextureUnit {
using Type = TextureUnit;
static const constexpr Type Default = 0;
static void Set(const Type&);
@@ -239,6 +240,12 @@ struct BindVertexArray {
static Type Get(const Context&);
};
+struct VertexAttribute {
+ using Type = optional<gl::AttributeBinding>;
+ static const Type Default;
+ static void Set(const Type&, Context&, AttributeLocation);
+};
+
struct PixelStorePack {
using Type = PixelStorageType;
static const constexpr Type Default = { 4 };
diff --git a/src/mbgl/gl/vertex_array.cpp b/src/mbgl/gl/vertex_array.cpp
new file mode 100644
index 0000000000..68a500ac45
--- /dev/null
+++ b/src/mbgl/gl/vertex_array.cpp
@@ -0,0 +1,18 @@
+#include <mbgl/gl/vertex_array.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/gl.hpp>
+
+namespace mbgl {
+namespace gl {
+
+void VertexArray::bind(Context& context, BufferID indexBuffer, const AttributeBindingArray& bindings) {
+ context.bindVertexArray = state->vertexArray;
+ state->indexBuffer = indexBuffer;
+
+ for (AttributeLocation location = 0; location < MAX_ATTRIBUTES; ++location) {
+ state->bindings[location] = bindings[location];
+ }
+}
+
+} // namespace gl
+} // namespace mbgl
diff --git a/src/mbgl/gl/vertex_array.hpp b/src/mbgl/gl/vertex_array.hpp
new file mode 100644
index 0000000000..46c67017bb
--- /dev/null
+++ b/src/mbgl/gl/vertex_array.hpp
@@ -0,0 +1,73 @@
+#pragma once
+
+#include <mbgl/gl/object.hpp>
+#include <mbgl/gl/attribute.hpp>
+#include <mbgl/gl/state.hpp>
+#include <mbgl/gl/value.hpp>
+
+#include <array>
+#include <memory>
+
+namespace mbgl {
+namespace gl {
+
+class Context;
+
+class VertexArrayState {
+public:
+ VertexArrayState(UniqueVertexArray vertexArray_, Context& context)
+ : vertexArray(std::move(vertexArray_)),
+ bindings(makeBindings(context, std::make_index_sequence<MAX_ATTRIBUTES>())) {
+ }
+
+ void setDirty() {
+ indexBuffer.setDirty();
+ for (auto& binding : bindings) {
+ binding.setDirty();
+ }
+ }
+
+ UniqueVertexArray vertexArray;
+ State<value::BindElementBuffer> indexBuffer;
+
+ using AttributeState = State<value::VertexAttribute, Context&, AttributeLocation>;
+ std::array<AttributeState, MAX_ATTRIBUTES> bindings;
+
+private:
+ template <std::size_t... I>
+ std::array<AttributeState, MAX_ATTRIBUTES> makeBindings(Context& context, std::index_sequence<I...>) {
+ return {{ AttributeState { context, I }... }};
+ }
+};
+
+class VertexArrayStateDeleter {
+public:
+ VertexArrayStateDeleter(bool destroy_)
+ : destroy(destroy_) {}
+
+ void operator()(VertexArrayState* ptr) const {
+ if (destroy) {
+ delete ptr;
+ }
+ }
+
+private:
+ bool destroy;
+};
+
+using UniqueVertexArrayState = std::unique_ptr<VertexArrayState, VertexArrayStateDeleter>;
+
+class VertexArray {
+public:
+ VertexArray(UniqueVertexArrayState state_)
+ : state(std::move(state_)) {
+ }
+
+ void bind(Context&, BufferID indexBuffer, const AttributeBindingArray&);
+
+private:
+ UniqueVertexArrayState state;
+};
+
+} // namespace gl
+} // namespace mbgl
diff --git a/src/mbgl/gl/vertex_buffer.hpp b/src/mbgl/gl/vertex_buffer.hpp
index 4808803d00..9f8b156b25 100644
--- a/src/mbgl/gl/vertex_buffer.hpp
+++ b/src/mbgl/gl/vertex_buffer.hpp
@@ -28,6 +28,7 @@ public:
bool empty() const { return v.empty(); }
void clear() { v.clear(); }
const Vertex* data() const { return v.data(); }
+ const std::vector<Vertex>& vector() const { return v; }
private:
std::vector<Vertex> v;
diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp
index ffb70c7ca2..6e152349ca 100644
--- a/src/mbgl/layout/symbol_instance.cpp
+++ b/src/mbgl/layout/symbol_instance.cpp
@@ -5,46 +5,48 @@ namespace mbgl {
using namespace style;
-SymbolInstance::SymbolInstance(Anchor& anchor,
- const GeometryCoordinates& line,
+SymbolInstance::SymbolInstance(Anchor& anchor_,
+ GeometryCoordinates line_,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
- const bool addToBuffers,
const uint32_t index_,
const float textBoxScale,
const float textPadding,
const SymbolPlacementType textPlacement,
+ 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_) :
- point(anchor.point),
+ const std::size_t featureIndex_,
+ const std::u16string& key_,
+ const float overscaling) :
+ anchor(anchor_),
+ line(line_),
index(index_),
hasText(shapedTextOrientations.first || shapedTextOrientations.second),
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),
- featureIndex(featureIndex_) {
+ textCollisionFeature(line_, anchor, shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature, overscaling),
+ iconCollisionFeature(line_, anchor, shapedIcon, iconBoxScale, iconPadding, indexedFeature),
+ featureIndex(featureIndex_),
+ textOffset(textOffset_),
+ iconOffset(iconOffset_),
+ key(key_) {
// Create the quads used for rendering the icon and glyphs.
- if (addToBuffers) {
- if (shapedIcon) {
- iconQuad = getIconQuad(anchor, *shapedIcon, line, layout, layoutTextSize, iconPlacement, shapedTextOrientations.first);
- }
- if (shapedTextOrientations.first) {
- auto quads = getGlyphQuads(anchor, shapedTextOrientations.first, textBoxScale, line, layout, textPlacement, positions);
- glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
- }
- if (shapedTextOrientations.second) {
- auto quads = getGlyphQuads(anchor, shapedTextOrientations.second, textBoxScale, line, layout, textPlacement, positions);
- glyphQuads.insert(glyphQuads.end(), quads.begin(), quads.end());
- }
+ if (shapedIcon) {
+ iconQuad = getIconQuad(*shapedIcon, layout, layoutTextSize, shapedTextOrientations.first);
+ }
+ if (shapedTextOrientations.first) {
+ horizontalGlyphQuads = getGlyphQuads(shapedTextOrientations.first, layout, textPlacement, positions);
+ }
+ if (shapedTextOrientations.second) {
+ verticalGlyphQuads = getGlyphQuads(shapedTextOrientations.second, layout, textPlacement, positions);
}
if (shapedTextOrientations.first && shapedTextOrientations.second) {
diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp
index f199d929df..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;
@@ -13,33 +14,45 @@ class IndexedSubfeature;
class SymbolInstance {
public:
SymbolInstance(Anchor& anchor,
- const GeometryCoordinates& line,
+ GeometryCoordinates line,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
- const bool inside,
const uint32_t index,
const float textBoxScale,
const float textPadding,
style::SymbolPlacementType textPlacement,
+ 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);
- Point<float> point;
+ Anchor anchor;
+ GeometryCoordinates line;
uint32_t index;
bool hasText;
bool hasIcon;
- SymbolQuads glyphQuads;
+ SymbolQuads horizontalGlyphQuads;
+ SymbolQuads verticalGlyphQuads;
optional<SymbolQuad> iconQuad;
CollisionFeature textCollisionFeature;
CollisionFeature iconCollisionFeature;
WritingModeType writingModes;
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 adc7eaaed8..fc1ede56ff 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>
@@ -43,8 +42,8 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
std::unique_ptr<GeometryTileLayer> sourceLayer_,
ImageDependencies& imageDependencies,
GlyphDependencies& glyphDependencies)
- : sourceLayer(std::move(sourceLayer_)),
- bucketName(layers.at(0)->getID()),
+ : bucketName(layers.at(0)->getID()),
+ sourceLayer(std::move(sourceLayer_)),
overscaling(parameters.tileID.overscaleFactor()),
zoom(parameters.tileID.overscaledZ),
mode(parameters.mode),
@@ -75,10 +74,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
}
}
- // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment`
+ // If unspecified `*-pitch-alignment` inherits `*-rotation-alignment`
if (layout.get<TextPitchAlignment>() == AlignmentType::Auto) {
layout.get<TextPitchAlignment>() = layout.get<TextRotationAlignment>();
}
+ if (layout.get<IconPitchAlignment>() == AlignmentType::Auto) {
+ layout.get<IconPitchAlignment>() = layout.get<IconRotationAlignment>();
+ }
const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty();
const bool hasIcon = has<IconImage>(layout);
@@ -176,48 +178,8 @@ bool SymbolLayout::hasSymbolInstances() const {
}
void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions,
- const ImageMap& imageMap, const ImagePositions& imagePositions) {
- float horizontalAlign = 0.5;
- float verticalAlign = 0.5;
-
- switch (layout.get<TextAnchor>()) {
- case TextAnchorType::Top:
- case TextAnchorType::Bottom:
- case TextAnchorType::Center:
- break;
- case TextAnchorType::Right:
- case TextAnchorType::TopRight:
- case TextAnchorType::BottomRight:
- horizontalAlign = 1;
- break;
- case TextAnchorType::Left:
- case TextAnchorType::TopLeft:
- case TextAnchorType::BottomLeft:
- horizontalAlign = 0;
- break;
- }
-
- switch (layout.get<TextAnchor>()) {
- case TextAnchorType::Left:
- case TextAnchorType::Right:
- case TextAnchorType::Center:
- break;
- case TextAnchorType::Bottom:
- case TextAnchorType::BottomLeft:
- case TextAnchorType::BottomRight:
- verticalAlign = 1;
- break;
- case TextAnchorType::Top:
- case TextAnchorType::TopLeft:
- case TextAnchorType::TopRight:
- verticalAlign = 0;
- break;
- }
-
- const float justify = layout.get<TextJustify>() == TextJustifyType::Right ? 1 :
- layout.get<TextJustify>() == TextJustifyType::Left ? 0 :
- 0.5;
-
+ const ImageMap& imageMap, const ImagePositions& imagePositions,
+ const OverscaledTileID& tileID, const std::string& sourceID) {
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
@@ -243,12 +205,11 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
const Shaping result = getShaping(
/* string */ text,
/* maxWidth: ems */ layout.get<SymbolPlacement>() != SymbolPlacementType::Line ?
- layout.get<TextMaxWidth>() * oneEm : 0,
+ layout.evaluate<TextMaxWidth>(zoom, feature) * oneEm : 0,
/* lineHeight: ems */ layout.get<TextLineHeight>() * oneEm,
- /* horizontalAlign */ horizontalAlign,
- /* verticalAlign */ verticalAlign,
- /* justify */ justify,
- /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get<TextLetterSpacing>() * oneEm : 0.0f,
+ /* anchor */ layout.evaluate<TextAnchor>(zoom, feature),
+ /* justify */ layout.evaluate<TextJustify>(zoom, feature),
+ /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.evaluate<TextLetterSpacing>(zoom, feature) * oneEm : 0.0f,
/* translate */ Point<float>(layout.evaluate<TextOffset>(zoom, feature)[0] * oneEm, layout.evaluate<TextOffset>(zoom, feature)[1] * oneEm),
/* verticalHeight */ oneEm,
/* writingMode */ writingMode,
@@ -272,6 +233,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
shapedIcon = PositionedIcon::shapeIcon(
imagePositions.at(*feature.icon),
layout.evaluate<IconOffset>(zoom, feature),
+ layout.evaluate<IconAnchor>(zoom, feature),
layout.evaluate<IconRotate>(zoom, feature) * util::DEG2RAD);
if (image->second->sdf) {
sdfIcons = true;
@@ -286,7 +248,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
// if either shapedText or icon position is present, add the feature
if (shapedTextOrientations.first || shapedIcon) {
- addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap);
+ addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID);
}
feature.geometry.clear();
@@ -299,12 +261,16 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolFeature& feature,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap& glyphPositionMap) {
+ const GlyphPositionMap& glyphPositionMap,
+ const OverscaledTileID& tileID,
+ const std::string& sourceID) {
const float minScale = 0.5f;
const float glyphSize = 24.0f;
const float layoutTextSize = layout.evaluate<TextSize>(zoom + 1, feature);
const float layoutIconSize = layout.evaluate<IconSize>(zoom + 1, feature);
+ const std::array<float, 2> textOffset = layout.evaluate<TextOffset>(zoom, feature);
+ const std::array<float, 2> iconOffset = layout.evaluate<IconOffset>(zoom, feature);
// To reduce the number of labels that jump around when zooming we need
// to use a text-size value that is the same for all zoom levels.
@@ -324,12 +290,10 @@ void SymbolLayout::addFeature(const std::size_t index,
const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map
? SymbolPlacementType::Point
: layout.get<SymbolPlacement>();
- const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
+
const float textRepeatDistance = symbolSpacing / 2;
- IndexedSubfeature indexedFeature = { feature.index, sourceLayer->getName(), bucketName,
- symbolInstances.size() };
+ IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size(),
+ sourceID, tileID.canonical);
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
@@ -350,14 +314,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,
- iconBoxScale, iconPadding, iconPlacement,
- 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();
@@ -428,94 +392,93 @@ bool SymbolLayout::anchorIsTooClose(const std::u16string& text, const float repe
return false;
}
-std::unique_ptr<SymbolBucket> SymbolLayout::place(CollisionTile& collisionTile) {
- auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear);
-
- // Calculate which labels can be shown and when they can be shown and
- // create the bufers used for rendering.
-
- const SymbolPlacementType textPlacement = layout.get<TextRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
- const SymbolPlacementType iconPlacement = layout.get<IconRotationAlignment>() != AlignmentType::Map
- ? SymbolPlacementType::Point
- : layout.get<SymbolPlacement>();
+// Analog of `addToLineVertexArray` in JS. This version doesn't need to build up a line array like the
+// JS version does, but it uses the same logic to calculate tile distances.
+std::vector<float> CalculateTileDistances(const GeometryCoordinates& line, const Anchor& anchor) {
+ std::vector<float> tileDistances(line.size());
+ if (anchor.segment != -1) {
+ auto sumForwardLength = util::dist<float>(anchor.point, line[anchor.segment + 1]);
+ auto sumBackwardLength = util::dist<float>(anchor.point, line[anchor.segment]);
+ for (size_t i = anchor.segment + 1; i < line.size(); i++) {
+ tileDistances[i] = sumForwardLength;
+ if (i < line.size() - 1) {
+ sumForwardLength += util::dist<float>(line[i + 1], line[i]);
+ }
+ }
+ for (auto i = anchor.segment; i >= 0; i--) {
+ tileDistances[i] = sumBackwardLength;
+ if (i > 0) {
+ sumBackwardLength += util::dist<float>(line[i - 1], line[i]);
+ }
+ }
+ }
+ return tileDistances;
+}
+std::unique_ptr<SymbolBucket> SymbolLayout::place(const bool showCollisionBoxes) {
const bool mayOverlap = layout.get<TextAllowOverlap>() || layout.get<IconAllowOverlap>() ||
layout.get<TextIgnorePlacement>() || layout.get<IconIgnorePlacement>();
+
+ auto bucket = std::make_unique<SymbolBucket>(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, std::move(symbolInstances));
- const bool keepUpright = layout.get<TextKeepUpright>();
-
- // Sort symbols by their y position on the canvas so that they lower symbols
- // are drawn on top of higher symbols.
- // Don't sort symbols that won't overlap because it isn't necessary and
- // because it causes more labels to pop in and out when rotating.
- if (mayOverlap) {
- const float sin = std::sin(collisionTile.config.angle);
- const float cos = std::cos(collisionTile.config.angle);
-
- std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
- const int32_t aRotated = sin * a.point.x + cos * a.point.y;
- const int32_t bRotated = sin * b.point.x + cos * b.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) {
- for (const auto& symbol : symbolInstance.glyphQuads) {
- addSymbol(
- bucket->text, *bucket->textSizeBinder, symbol, feature, placementZoom,
- keepUpright, textPlacement, collisionTile.config.angle, symbolInstance.writingModes);
+ 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, 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) {
- addSymbol(
- bucket->icon, *bucket->iconSizeBinder, *symbolInstance.iconQuad, feature, placementZoom,
- keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes);
+ 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, 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);
}
}
@@ -525,23 +488,19 @@ 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,
- SymbolSizeBinder& sizeBinder,
+size_t SymbolLayout::addSymbol(Buffer& buffer,
+ const Range<float> sizeData,
const SymbolQuad& symbol,
- const SymbolFeature& feature,
- const float placementZoom,
- const bool keepUpright,
- const style::SymbolPlacementType placement,
- const float placementAngle,
- const WritingModeType writingModes) {
+ const Anchor& labelAnchor,
+ PlacedSymbol& placedSymbol) {
constexpr const uint16_t vertexLength = 4;
const auto &tl = symbol.tl;
@@ -550,32 +509,6 @@ void SymbolLayout::addSymbol(Buffer& buffer,
const auto &br = symbol.br;
const auto &tex = symbol.tex;
- float minZoom = util::max(zoom + util::log2(symbol.minScale), placementZoom);
- float maxZoom = util::min(zoom + util::log2(symbol.maxScale), util::MAX_ZOOM_F);
- const auto &anchorPoint = symbol.anchorPoint;
-
- // drop incorrectly oriented glyphs
- const float a = std::fmod(symbol.anchorAngle + placementAngle + M_PI, M_PI * 2);
- if (writingModes & WritingModeType::Vertical) {
- if (placement == style::SymbolPlacementType::Line && symbol.writingMode == WritingModeType::Vertical) {
- if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 5 / 4) || a > (M_PI * 7 / 4)))
- return;
- } else if (keepUpright && placement == style::SymbolPlacementType::Line && (a <= (M_PI * 3 / 4) || a > (M_PI * 5 / 4)))
- return;
- } else if (keepUpright && placement == style::SymbolPlacementType::Line &&
- (a <= M_PI / 2 || a > M_PI * 3 / 2)) {
- return;
- }
-
- if (maxZoom <= minZoom)
- return;
-
- // Lower min zoom so that while fading out the label
- // it can be shown outside of collision-free zoom levels
- if (minZoom == placementZoom) {
- minZoom = 0;
- }
-
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());
}
@@ -586,20 +519,25 @@ void SymbolLayout::addSymbol(Buffer& buffer,
assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max());
uint16_t index = segment.vertexLength;
- // Encode angle of glyph
- uint8_t glyphAngle = std::round((symbol.glyphAngle / (M_PI * 2)) * 256);
-
// coordinates (2 triangles)
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tl, tex.x, tex.y,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, tr, tex.x + tex.w, tex.y,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, bl, tex.x, tex.y + tex.h,
- minZoom, maxZoom, placementZoom, glyphAngle));
- buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(anchorPoint, br, tex.x + tex.w, tex.y + tex.h,
- minZoom, maxZoom, placementZoom, glyphAngle));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tl, symbol.glyphOffset.y, tex.x, tex.y, sizeData));
+ buffer.vertices.emplace_back(SymbolLayoutAttributes::vertex(labelAnchor.point, tr, symbol.glyphOffset.y, tex.x + tex.w, tex.y, sizeData));
+ 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));
+
+ // 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);
- sizeBinder.populateVertexVector(feature);
+ 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);
@@ -607,54 +545,64 @@ void SymbolLayout::addSymbol(Buffer& buffer,
segment.vertexLength += vertexLength;
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, tl, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, tr, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, br, maxZoom, placementZoom));
- collisionBox.vertices.emplace_back(CollisionBoxProgram::vertex(anchor, 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 8cdaadff00..6951c29ada 100644
--- a/src/mbgl/layout/symbol_layout.hpp
+++ b/src/mbgl/layout/symbol_layout.hpp
@@ -16,10 +16,10 @@
namespace mbgl {
class BucketParameters;
-class CollisionTile;
class SymbolBucket;
class Anchor;
class RenderLayer;
+class PlacedSymbol;
namespace style {
class Filter;
@@ -34,50 +34,44 @@ public:
GlyphDependencies&);
void prepare(const GlyphMap&, const GlyphPositions&,
- const ImageMap&, const ImagePositions&);
+ const ImageMap&, const ImagePositions&,
+ const OverscaledTileID&, const std::string&);
- std::unique_ptr<SymbolBucket> place(CollisionTile&);
+ std::unique_ptr<SymbolBucket> place(const bool showCollisionBoxes);
bool hasSymbolInstances() const;
- enum State {
- Pending, // Waiting for the necessary glyphs or icons to be available.
- Placed // The final positions have been determined, taking into account prior layers.
- };
-
- State state = Pending;
-
std::map<std::string,
std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>> layerPaintProperties;
+ const std::string bucketName;
+ std::vector<SymbolInstance> symbolInstances;
+
private:
void addFeature(const size_t,
const SymbolFeature&,
const std::pair<Shaping, Shaping>& shapedTextOrientations,
optional<PositionedIcon> shapedIcon,
- const GlyphPositionMap&);
+ const GlyphPositionMap&,
+ const OverscaledTileID&,
+ const std::string&);
bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&);
std::map<std::u16string, std::vector<Anchor>> compareText;
- void addToDebugBuffers(CollisionTile&, SymbolBucket&);
+ void addToDebugBuffers(SymbolBucket&);
// Adds placed items to the buffer.
template <typename Buffer>
- void addSymbol(Buffer&,
- SymbolSizeBinder& sizeBinder,
+ size_t addSymbol(Buffer&,
+ const Range<float> sizeData,
const SymbolQuad&,
- const SymbolFeature& feature,
- float scale,
- const bool keepUpright,
- const style::SymbolPlacementType,
- const float placementAngle,
- WritingModeType writingModes);
+ 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;
@@ -94,7 +88,6 @@ private:
style::TextSize::UnevaluatedType textSize;
style::IconSize::UnevaluatedType iconSize;
- std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;
BiDi bidi; // Consider moving this up to geometry tile worker to reduce reinstantiation costs; use of BiDi/ubiditransform object must be constrained to one thread
diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp
new file mode 100644
index 0000000000..ee6385c93c
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.cpp
@@ -0,0 +1,420 @@
+#include <mbgl/layout/symbol_projection.hpp>
+#include <mbgl/map/transform_state.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/math.hpp>
+
+namespace mbgl {
+
+ /*
+ * # Overview of coordinate spaces
+ *
+ * ## Tile coordinate spaces
+ * Each label has an anchor. Some labels have corresponding line geometries.
+ * The points for both anchors and lines are stored in tile units. Each tile has it's own
+ * coordinate space going from (0, 0) at the top left to (EXTENT, EXTENT) at the bottom right.
+ *
+ * ## GL coordinate space
+ * At the end of everything, the vertex shader needs to produce a position in GL coordinate space,
+ * which is (-1, 1) at the top left and (1, -1) in the bottom right.
+ *
+ * ## Map pixel coordinate spaces
+ * Each tile has a pixel coordinate space. It's just the tile units scaled so that one unit is
+ * whatever counts as 1 pixel at the current zoom.
+ * This space is used for pitch-alignment=map, rotation-alignment=map
+ *
+ * ## Rotated map pixel coordinate spaces
+ * Like the above, but rotated so axis of the space are aligned with the viewport instead of the tile.
+ * This space is used for pitch-alignment=map, rotation-alignment=viewport
+ *
+ * ## Viewport pixel coordinate space
+ * (0, 0) is at the top left of the canvas and (pixelWidth, pixelHeight) is at the bottom right corner
+ * of the canvas. This space is used for pitch-alignment=viewport
+ *
+ *
+ * # Vertex projection
+ * It goes roughly like this:
+ * 1. project the anchor and line from tile units into the correct label coordinate space
+ * - map pixel space pitch-alignment=map rotation-alignment=map
+ * - rotated map pixel space pitch-alignment=map rotation-alignment=viewport
+ * - viewport pixel space pitch-alignment=viewport rotation-alignment=*
+ * 2. if the label follows a line, find the point along the line that is the correct distance from the anchor.
+ * 3. add the glyph's corner offset to the point from step 3
+ * 4. convert from the label coordinate space to gl coordinates
+ *
+ * For horizontal labels we want to do step 1 in the shader for performance reasons (no cpu work).
+ * This is what `u_label_plane_matrix` is used for.
+ * For labels aligned with lines we have to steps 1 and 2 on the cpu since we need access to the line geometry.
+ * This is what `updateLineLabels(...)` does.
+ * Since the conversion is handled on the cpu we just set `u_label_plane_matrix` to an identity matrix.
+ *
+ * Steps 3 and 4 are done in the shaders for all labels.
+ */
+
+ /*
+ * Returns a matrix for converting from tile units to the correct label coordinate space.
+ */
+ mat4 getLabelPlaneMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) {
+ mat4 m;
+ matrix::identity(m);
+ if (pitchWithMap) {
+ matrix::scale(m, m, 1 / pixelsToTileUnits, 1 / pixelsToTileUnits, 1);
+ if (!rotateWithMap) {
+ matrix::rotate_z(m, m, state.getAngle());
+ }
+ } else {
+ matrix::scale(m, m, state.getSize().width / 2.0, -(state.getSize().height / 2.0), 1.0);
+ matrix::translate(m, m, 1, -1, 0);
+ matrix::multiply(m, m, posMatrix);
+ }
+ return m;
+ }
+
+ /*
+ * Returns a matrix for converting from the correct label coordinate space to gl coords.
+ */
+ mat4 getGlCoordMatrix(const mat4& posMatrix, const bool pitchWithMap, const bool rotateWithMap, const TransformState& state, const float pixelsToTileUnits) {
+ mat4 m;
+ matrix::identity(m);
+ if (pitchWithMap) {
+ matrix::multiply(m, m, posMatrix);
+ matrix::scale(m, m, pixelsToTileUnits, pixelsToTileUnits, 1);
+ if (!rotateWithMap) {
+ matrix::rotate_z(m, m, -state.getAngle());
+ }
+ } else {
+ matrix::scale(m, m, 1, -1, 1);
+ matrix::translate(m, m, -1, -1, 0);
+ matrix::scale(m, m, 2.0 / state.getSize().width, 2.0 / state.getSize().height, 1.0);
+ }
+ return m;
+ }
+
+ PointAndCameraDistance project(const Point<float>& point, const mat4& matrix) {
+ vec4 pos = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(pos, pos, matrix);
+ return {{ static_cast<float>(pos[0] / pos[3]), static_cast<float>(pos[1] / pos[3]) }, pos[3] };
+ }
+
+ float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol) {
+ if (zoomEvaluatedSize.isFeatureConstant) {
+ return zoomEvaluatedSize.size;
+ } else {
+ if (zoomEvaluatedSize.isZoomConstant) {
+ return placedSymbol.lowerSize;
+ } else {
+ return placedSymbol.lowerSize + zoomEvaluatedSize.sizeT * (placedSymbol.upperSize - placedSymbol.lowerSize);
+ }
+ }
+ }
+
+ 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 = (
+ x >= -clippingBuffer[0] &&
+ x <= clippingBuffer[0] &&
+ y >= -clippingBuffer[1] &&
+ y <= clippingBuffer[1]);
+ return inPaddedViewport;
+ }
+
+ void addDynamicAttributes(const Point<float>& anchorPoint, const float angle,
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray) {
+ auto dynamicVertex = SymbolDynamicLayoutAttributes::vertex(anchorPoint, angle);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ dynamicVertexArray.emplace_back(dynamicVertex);
+ }
+
+ 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, dynamicVertexArray);
+ }
+ }
+
+ enum PlacementResult {
+ OK,
+ NotEnoughRoom,
+ NeedsFlipping,
+ UseVertical
+ };
+
+ Point<float> projectTruncatedLineSegment(const Point<float>& previousTilePoint, const Point<float>& currentTilePoint, const Point<float>& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) {
+ // We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane
+ // If it did, that would mean our label extended all the way out from within the viewport to a (very distant)
+ // point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the
+ // plane of the camera.
+ const Point<float> projectedUnitVertex = project(previousTilePoint + util::unit<float>(previousTilePoint - currentTilePoint), projectionMatrix).first;
+ const Point<float> projectedUnitSegment = previousProjectedPoint - projectedUnitVertex;
+
+ return previousProjectedPoint + (projectedUnitSegment * (minimumLength / util::mag<float>(projectedUnitSegment)));
+ }
+
+ 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 std::vector<float>& tileDistances, const mat4& labelPlaneMatrix, const bool returnTileDistance) {
+
+ const float combinedOffsetX = flip ?
+ offsetX - lineOffsetX :
+ offsetX + lineOffsetX;
+
+ int16_t dir = combinedOffsetX > 0 ? 1 : -1;
+
+ float angle = 0.0;
+ if (flip) {
+ // The label needs to be flipped to keep text upright.
+ // Iterate in the reverse direction.
+ dir *= -1;
+ angle = M_PI;
+ }
+
+ if (dir < 0) angle += M_PI;
+
+ 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;
+ float currentSegmentDistance = 0.0;
+ const float absOffsetX = std::abs(combinedOffsetX);
+
+ while (distanceToPrev + currentSegmentDistance <= absOffsetX) {
+ currentIndex += dir;
+
+ // offset does not fit on the projected line
+ if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) {
+ return {};
+ }
+
+ prev = current;
+ PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix);
+ if (projection.second > 0) {
+ current = projection.first;
+ } else {
+ // The vertex is behind the plane of the camera, so we can't project it
+ // Instead, we'll create a vertex along the line that's far enough to include the glyph
+ const Point<float> previousTilePoint = distanceToPrev == 0 ?
+ tileAnchorPoint :
+ convertPoint<float>(line.at(currentIndex - dir));
+ const Point<float> currentTilePoint = convertPoint<float>(line.at(currentIndex));
+ current = projectTruncatedLineSegment(previousTilePoint, currentTilePoint, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix);
+ }
+
+ distanceToPrev += currentSegmentDistance;
+ currentSegmentDistance = util::dist<float>(prev, current);
+ }
+
+ // The point is on the current segment. Interpolate to find it.
+ const float segmentInterpolationT = (absOffsetX - distanceToPrev) / currentSegmentDistance;
+ const Point<float> prevToCurrent = current - prev;
+ Point<float> p = (prevToCurrent * segmentInterpolationT) + prev;
+
+ // offset the point from the line to text-offset and icon-offset
+ p += util::perp(prevToCurrent) * static_cast<float>(lineOffsetY * dir / util::mag(prevToCurrent));
+
+ const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x);
+
+ return {{
+ p,
+ segmentAngle,
+ returnTileDistance ?
+ TileDistance(
+ (currentIndex - dir) == initialIndex ? 0 : tileDistances[currentIndex - dir],
+ absOffsetX - distanceToPrev
+ ) :
+ optional<TileDistance>()
+ }};
+ }
+
+ optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale,
+ const float lineOffsetX,
+ const float lineOffsetY,
+ const bool flip,
+ const Point<float>& anchorPoint,
+ const Point<float>& tileAnchorPoint,
+ const PlacedSymbol& symbol,
+ const mat4& labelPlaneMatrix,
+ const bool returnTileDistance) {
+
+ const float firstGlyphOffset = symbol.glyphOffsets.front();
+ const float lastGlyphOffset = symbol.glyphOffsets.back();;
+
+ optional<PlacedGlyph> firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance);
+ if (!firstPlacedGlyph)
+ return optional<std::pair<PlacedGlyph, PlacedGlyph>>();
+
+ optional<PlacedGlyph> lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, tileAnchorPoint, symbol.segment, symbol.line, symbol.tileDistances, labelPlaneMatrix, returnTileDistance);
+ if (!lastPlacedGlyph)
+ return optional<std::pair<PlacedGlyph, PlacedGlyph>>();
+
+ return std::make_pair(*firstPlacedGlyph, *lastPlacedGlyph);
+ }
+
+ optional<PlacementResult> requiresOrientationChange(const WritingModeType writingModes, const Point<float>& firstPoint, const Point<float>& lastPoint, const float aspectRatio) {
+ if (writingModes == (WritingModeType::Horizontal | WritingModeType::Vertical)) {
+ // On top of choosing whether to flip, choose whether to render this version of the glyphs or the alternate
+ // vertical glyphs. We can't just filter out vertical glyphs in the horizontal range because the horizontal
+ // and vertical versions can have slightly different projections which could lead to angles where both or
+ // neither showed.
+ auto rise = std::abs(lastPoint.y - firstPoint.y);
+ auto run = std::abs(lastPoint.x - firstPoint.x) * aspectRatio;
+ if (rise > run) {
+ return PlacementResult::UseVertical;
+ }
+ }
+
+ if ((writingModes == WritingModeType::Vertical) ?
+ (firstPoint.y < lastPoint.y) :
+ (firstPoint.x > lastPoint.x)) {
+ // Includes "horizontalOnly" case for labels without vertical glyphs
+ return PlacementResult::NeedsFlipping;
+ }
+ return {};
+ }
+
+ PlacementResult placeGlyphsAlongLine(const PlacedSymbol& symbol,
+ const float fontSize,
+ const bool flip,
+ const bool keepUpright,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const mat4& glCoordMatrix,
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex>& dynamicVertexArray,
+ 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;
+
+ std::vector<PlacedGlyph> placedGlyphs;
+ if (symbol.glyphOffsets.size() > 1) {
+
+ 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(firstAndLastGlyph->first.point, glCoordMatrix).first;
+ const Point<float> lastPoint = project(firstAndLastGlyph->second.point, glCoordMatrix).first;
+
+ if (keepUpright && !flip) {
+ auto orientationChange = requiresOrientationChange(symbol.writingModes, firstPoint, lastPoint, aspectRatio);
+ if (orientationChange) {
+ return *orientationChange;
+ }
+ }
+
+ 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, symbol.tileDistances, labelPlaneMatrix, false);
+ placedGlyphs.push_back(*placedGlyph);
+ }
+ placedGlyphs.push_back(firstAndLastGlyph->second);
+ } else {
+ // Only a single glyph to place
+ // So, determine whether to flip based on projected angle of the line segment it's on
+ if (keepUpright && !flip) {
+ const Point<float> a = project(symbol.anchorPoint, posMatrix).first;
+ const Point<float> tileSegmentEnd = convertPoint<float>(symbol.line.at(symbol.segment + 1));
+ const PointAndCameraDistance projectedVertex = project(tileSegmentEnd, posMatrix);
+ // We know the anchor will be in the viewport, but the end of the line segment may be
+ // behind the plane of the camera, in which case we can use a point at any arbitrary (closer)
+ // point on the segment.
+ const Point<float> b = (projectedVertex.second > 0) ?
+ projectedVertex.first :
+ projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix);
+
+ auto orientationChange = requiresOrientationChange(symbol.writingModes, a, b, aspectRatio);
+ if (orientationChange) {
+ return *orientationChange;
+ }
+ }
+ assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all
+ const float glyphOffsetX = symbol.glyphOffsets.front();
+ optional<PlacedGlyph> singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment,
+ symbol.line, symbol.tileDistances, labelPlaneMatrix, false);
+ if (!singleGlyph)
+ return PlacementResult::NotEnoughRoom;
+
+ placedGlyphs.push_back(*singleGlyph);
+ }
+
+ for (auto& placedGlyph : placedGlyphs) {
+ addDynamicAttributes(placedGlyph.point, placedGlyph.angle, dynamicVertexArray);
+ }
+
+ return PlacementResult::OK;
+ }
+
+
+ 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 ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom());
+
+ const std::array<double, 2> clippingBuffer = {{ 256.0 / state.getSize().width * 2.0 + 1.0, 256.0 / state.getSize().height * 2.0 + 1.0 }};
+
+ const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map;
+ const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map;
+ const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1, state.getZoom());
+
+ const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, pitchWithMap,
+ rotateWithMap, state, pixelsToTileUnits);
+
+ 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, clippingBuffer)) {
+ hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
+ continue;
+ }
+
+ const float cameraToAnchorDistance = anchorPos[3];
+ const float perspectiveRatio = 0.5 + 0.5 * (cameraToAnchorDistance / state.getCameraToCenterDistance());
+
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol);
+ const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ?
+ fontSize * perspectiveRatio :
+ fontSize / perspectiveRatio;
+
+ const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first;
+
+ 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 || useVertical ||
+ (placeUnflipped == PlacementResult::NeedsFlipping &&
+ placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) {
+ hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
+ }
+ }
+ }
+} // end namespace mbgl
diff --git a/src/mbgl/layout/symbol_projection.hpp b/src/mbgl/layout/symbol_projection.hpp
new file mode 100644
index 0000000000..3e57d162fd
--- /dev/null
+++ b/src/mbgl/layout/symbol_projection.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/gl/vertex_buffer.hpp>
+#include <mbgl/programs/symbol_program.hpp>
+
+namespace mbgl {
+
+ class TransformState;
+ class RenderTile;
+ 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&);
+
+ 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/backend_scope.cpp b/src/mbgl/map/backend_scope.cpp
deleted file mode 100644
index 824ad4498b..0000000000
--- a/src/mbgl/map/backend_scope.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/map/backend.hpp>
-#include <mbgl/util/thread_local.hpp>
-
-#include <cassert>
-
-namespace mbgl {
-
-static util::ThreadLocal<BackendScope> currentScope;
-
-BackendScope::BackendScope(Backend& backend_, ScopeType scopeType_)
- : priorScope(currentScope.get()),
- nextScope(nullptr),
- backend(backend_),
- scopeType(scopeType_) {
- if (priorScope) {
- assert(priorScope->nextScope == nullptr);
- priorScope->nextScope = this;
- }
- if (scopeType == ScopeType::Explicit) {
- backend.activate();
- }
-
- currentScope.set(this);
-}
-
-BackendScope::~BackendScope() {
- assert(nextScope == nullptr);
- if (priorScope) {
- priorScope->backend.activate();
- currentScope.set(priorScope);
- assert(priorScope->nextScope == this);
- priorScope->nextScope = nullptr;
- } else {
- if (scopeType == ScopeType::Explicit) {
- backend.deactivate();
- }
-
- currentScope.set(nullptr);
- }
-}
-
-bool BackendScope::exists() {
- return currentScope.get();
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 37442770fa..947973415a 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -1,22 +1,19 @@
#include <mbgl/map/map.hpp>
#include <mbgl/map/camera.hpp>
-#include <mbgl/map/view.hpp>
-#include <mbgl/map/backend.hpp>
-#include <mbgl/map/backend_scope.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/style/style_impl.hpp>
#include <mbgl/style/observer.hpp>
#include <mbgl/renderer/update_parameters.hpp>
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_style.hpp>
-#include <mbgl/renderer/render_style_observer.hpp>
-#include <mbgl/util/exception.hpp>
+#include <mbgl/renderer/renderer_frontend.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
+#include <mbgl/util/constants.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/exception.hpp>
-#include <mbgl/util/async_task.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/actor/scheduler.hpp>
@@ -28,151 +25,132 @@ namespace mbgl {
using namespace style;
-enum class RenderState : uint8_t {
- Never,
- Partial,
- Fully,
-};
-
struct StillImageRequest {
- StillImageRequest(View& view_, Map::StillImageCallback&& callback_)
- : view(view_), callback(std::move(callback_)) {
+ StillImageRequest(Map::StillImageCallback&& callback_)
+ : callback(std::move(callback_)) {
}
- View& view;
Map::StillImageCallback callback;
};
class Map::Impl : public style::Observer,
- public RenderStyleObserver {
+ public RendererObserver {
public:
Impl(Map&,
- Backend&,
+ RendererFrontend&,
+ MapObserver&,
float pixelRatio,
FileSource&,
Scheduler&,
MapMode,
- GLContextMode,
ConstrainMode,
- ViewportMode,
- optional<std::string> programCacheDir);
+ ViewportMode);
+
+ ~Impl();
+ // StyleObserver
void onSourceChanged(style::Source&) override;
- void onUpdate(Update) override;
- void onInvalidate() override;
+ void onUpdate() override;
void onStyleLoading() override;
void onStyleLoaded() override;
void onStyleError(std::exception_ptr) override;
- void onResourceError(std::exception_ptr) override;
- void render(View&);
- void renderStill();
+ // RendererObserver
+ void onInvalidate() override;
+ void onResourceError(std::exception_ptr) override;
+ void onWillStartRenderingFrame() override;
+ void onDidFinishRenderingFrame(RenderMode, bool) override;
+ void onWillStartRenderingMap() override;
+ void onDidFinishRenderingMap() override;
Map& map;
MapObserver& observer;
- Backend& backend;
+ RendererFrontend& rendererFrontend;
FileSource& fileSource;
Scheduler& scheduler;
- RenderState renderState = RenderState::Never;
Transform transform;
const MapMode mode;
- const GLContextMode contextMode;
const float pixelRatio;
- const optional<std::string> programCacheDir;
MapDebugOptions debugOptions { MapDebugOptions::NoDebug };
- Update updateFlags = Update::Nothing;
-
- AnnotationManager annotationManager;
- std::unique_ptr<Painter> painter;
std::unique_ptr<Style> style;
- std::unique_ptr<RenderStyle> renderStyle;
+ AnnotationManager annotationManager;
bool cameraMutated = false;
- size_t sourceCacheSize;
- bool loading = false;
+ uint8_t prefetchZoomDelta = util::DEFAULT_PREFETCH_ZOOM_DELTA;
- util::AsyncTask asyncInvalidate;
+ bool loading = false;
+ bool rendererFullyLoaded;
std::unique_ptr<StillImageRequest> stillImageRequest;
};
-Map::Map(Backend& backend,
+Map::Map(RendererFrontend& rendererFrontend,
+ MapObserver& mapObserver,
const Size size,
const float pixelRatio,
FileSource& fileSource,
Scheduler& scheduler,
MapMode mapMode,
- GLContextMode contextMode,
ConstrainMode constrainMode,
- ViewportMode viewportMode,
- const optional<std::string>& programCacheDir)
+ ViewportMode viewportMode)
: impl(std::make_unique<Impl>(*this,
- backend,
+ rendererFrontend,
+ mapObserver,
pixelRatio,
fileSource,
scheduler,
mapMode,
- contextMode,
constrainMode,
- viewportMode,
- programCacheDir)) {
+ viewportMode)) {
impl->transform.resize(size);
}
Map::Impl::Impl(Map& map_,
- Backend& backend_,
+ RendererFrontend& frontend,
+ MapObserver& mapObserver,
float pixelRatio_,
FileSource& fileSource_,
Scheduler& scheduler_,
MapMode mode_,
- GLContextMode contextMode_,
ConstrainMode constrainMode_,
- ViewportMode viewportMode_,
- optional<std::string> programCacheDir_)
+ ViewportMode viewportMode_)
: map(map_),
- observer(backend_),
- backend(backend_),
+ observer(mapObserver),
+ rendererFrontend(frontend),
fileSource(fileSource_),
scheduler(scheduler_),
transform(observer,
constrainMode_,
viewportMode_),
mode(mode_),
- contextMode(contextMode_),
pixelRatio(pixelRatio_),
- programCacheDir(std::move(programCacheDir_)),
- asyncInvalidate([this] {
- if (mode == MapMode::Continuous) {
- backend.invalidate();
- } else {
- renderStill();
- }
- }) {
- style = std::make_unique<Style>(scheduler, fileSource, pixelRatio);
+ style(std::make_unique<Style>(scheduler, fileSource, pixelRatio)),
+ annotationManager(*style) {
+
style->impl->setObserver(this);
+ rendererFrontend.setObserver(*this);
}
-Map::~Map() {
- BackendScope guard(impl->backend);
+Map::Impl::~Impl() {
+ // Explicitly reset the RendererFrontend first to ensure it releases
+ // All shared resources (AnnotationManager)
+ rendererFrontend.reset();
+};
- // Explicit resets currently necessary because these abandon resources that need to be
- // cleaned up by context.reset();
- impl->renderStyle.reset();
- impl->painter.reset();
-}
+Map::~Map() = default;
-void Map::renderStill(View& view, StillImageCallback callback) {
+void Map::renderStill(StillImageCallback callback) {
if (!callback) {
Log::Error(Event::General, "StillImageCallback not set");
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;
}
@@ -186,131 +164,61 @@ void Map::renderStill(View& view, StillImageCallback callback) {
return;
}
- impl->stillImageRequest = std::make_unique<StillImageRequest>(view, std::move(callback));
- impl->onUpdate(Update::Repaint);
-}
-
-void Map::Impl::renderStill() {
- if (!stillImageRequest) {
- return;
- }
+ impl->stillImageRequest = std::make_unique<StillImageRequest>(std::move(callback));
- // TODO: determine whether we need activate/deactivate
- BackendScope guard(backend);
- render(stillImageRequest->view);
+ impl->onUpdate();
}
-void Map::triggerRepaint() {
- impl->backend.invalidate();
+void Map::renderStill(const CameraOptions& camera, MapDebugOptions debugOptions, StillImageCallback callback) {
+ impl->cameraMutated = true;
+ impl->debugOptions = debugOptions;
+ impl->transform.jumpTo(camera);
+ renderStill(std::move(callback));
}
-void Map::render(View& view) {
- impl->render(view);
+void Map::triggerRepaint() {
+ impl->onUpdate();
}
-void Map::Impl::render(View& view) {
- TimePoint timePoint = Clock::now();
-
- transform.updateTransitions(timePoint);
+#pragma mark - Map::Impl RendererObserver
- if (style->impl->loaded && updateFlags & Update::AnnotationStyle) {
- annotationManager.updateStyle(*style->impl);
- }
-
- if (updateFlags & Update::AnnotationData) {
- annotationManager.updateData();
+void Map::Impl::onWillStartRenderingMap() {
+ if (mode == MapMode::Continuous) {
+ observer.onWillStartRenderingMap();
}
+}
- updateFlags = Update::Nothing;
-
- gl::Context& context = backend.getContext();
- if (!painter) {
- renderStyle = std::make_unique<RenderStyle>(scheduler, fileSource);
- renderStyle->setObserver(this);
- painter = std::make_unique<Painter>(context, transform.getState(), pixelRatio, programCacheDir);
+void Map::Impl::onWillStartRenderingFrame() {
+ if (mode == MapMode::Continuous) {
+ observer.onWillStartRenderingFrame();
}
+}
- renderStyle->update({
- mode,
- pixelRatio,
- debugOptions,
- timePoint,
- transform.getState(),
- style->impl->getGlyphURL(),
- style->impl->spriteLoaded,
- style->getTransitionOptions(),
- style->getLight()->impl,
- style->impl->getImageImpls(),
- style->impl->getSourceImpls(),
- style->impl->getLayerImpls(),
- scheduler,
- fileSource,
- annotationManager
- });
-
- bool loaded = style->impl->isLoaded() && renderStyle->isLoaded();
+void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepaint) {
+ rendererFullyLoaded = renderMode == RenderMode::Full;
if (mode == MapMode::Continuous) {
- if (renderState == RenderState::Never) {
- observer.onWillStartRenderingMap();
- }
-
- observer.onWillStartRenderingFrame();
-
- FrameData frameData { timePoint,
- pixelRatio,
- mode,
- contextMode,
- debugOptions };
-
- backend.updateAssumedState();
-
- painter->render(*renderStyle,
- frameData,
- view);
-
- painter->cleanup();
-
- observer.onDidFinishRenderingFrame(loaded
- ? MapObserver::RenderMode::Full
- : MapObserver::RenderMode::Partial);
-
- if (!loaded) {
- renderState = RenderState::Partial;
- } else if (renderState != RenderState::Fully) {
- renderState = RenderState::Fully;
- observer.onDidFinishRenderingMap(MapObserver::RenderMode::Full);
- if (loading) {
- loading = false;
- observer.onDidFinishLoadingMap();
- }
- }
+ observer.onDidFinishRenderingFrame(MapObserver::RenderMode(renderMode));
- // Schedule an update if we need to paint another frame due to transitions or
- // animations that are still in progress
- if (renderStyle->hasTransitions() || painter->needsAnimation() || transform.inTransition()) {
- onUpdate(Update::Repaint);
+ if (needsRepaint || transform.inTransition()) {
+ onUpdate();
}
- } else if (stillImageRequest && loaded) {
- FrameData frameData { timePoint,
- pixelRatio,
- mode,
- contextMode,
- debugOptions };
-
- backend.updateAssumedState();
-
- painter->render(*renderStyle,
- frameData,
- view);
-
+ } else if (stillImageRequest && rendererFullyLoaded) {
auto request = std::move(stillImageRequest);
request->callback(nullptr);
-
- painter->cleanup();
}
}
+void Map::Impl::onDidFinishRenderingMap() {
+ if (mode == MapMode::Continuous && loading) {
+ observer.onDidFinishRenderingMap(MapObserver::RenderMode::Full);
+ if (loading) {
+ loading = false;
+ observer.onDidFinishLoadingMap();
+ }
+ }
+};
+
#pragma mark - Style
style::Style& Map::getStyle() {
@@ -322,20 +230,22 @@ const style::Style& Map::getStyle() const {
}
void Map::setStyle(std::unique_ptr<Style> style) {
+ assert(style);
impl->onStyleLoading();
impl->style = std::move(style);
+ impl->annotationManager.setStyle(*impl->style);
}
#pragma mark - Transitions
void Map::cancelTransitions() {
impl->transform.cancelTransitions();
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setGestureInProgress(bool inProgress) {
impl->transform.setGestureInProgress(inProgress);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
bool Map::isGestureInProgress() const {
@@ -363,19 +273,19 @@ CameraOptions Map::getCameraOptions(const EdgeInsets& padding) const {
void Map::jumpTo(const CameraOptions& camera) {
impl->cameraMutated = true;
impl->transform.jumpTo(camera);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.easeTo(camera, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.flyTo(camera, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
#pragma mark - Position
@@ -383,7 +293,7 @@ void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation)
void Map::moveBy(const ScreenCoordinate& point, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.moveBy(point, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) {
@@ -394,13 +304,13 @@ void Map::setLatLng(const LatLng& latLng, const AnimationOptions& animation) {
void Map::setLatLng(const LatLng& latLng, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLng(latLng, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLng(latLng, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
LatLng Map::getLatLng(const EdgeInsets& padding) const {
@@ -416,7 +326,7 @@ void Map::resetPosition(const EdgeInsets& padding) {
camera.padding = padding;
camera.zoom = 0;
impl->transform.jumpTo(camera);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
@@ -430,13 +340,13 @@ void Map::setZoom(double zoom, const AnimationOptions& animation) {
void Map::setZoom(double zoom, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setZoom(zoom, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setZoom(double zoom, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setZoom(zoom, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getZoom() const {
@@ -451,30 +361,30 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const AnimationOption
void Map::setLatLngZoom(const LatLng& latLng, double zoom, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setLatLngZoom(latLng, zoom, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
-CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding) const {
+CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, const EdgeInsets& padding, optional<double> bearing) const {
return cameraForLatLngs({
bounds.northwest(),
bounds.southwest(),
bounds.southeast(),
bounds.northeast(),
- }, padding);
+ }, padding, bearing);
}
-CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding) const {
+CameraOptions cameraForLatLngs(const std::vector<LatLng>& latLngs, const Transform& transform, const EdgeInsets& padding) {
CameraOptions options;
if (latLngs.empty()) {
return options;
}
-
+ Size size = transform.getState().getSize();
// Calculate the bounds of the possibly rotated shape with respect to the viewport.
ScreenCoordinate nePixel = {-INFINITY, -INFINITY};
ScreenCoordinate swPixel = {INFINITY, INFINITY};
- double viewportHeight = getSize().height;
+ double viewportHeight = size.height;
for (LatLng latLng : latLngs) {
- ScreenCoordinate pixel = impl->transform.latLngToScreenCoordinate(latLng);
+ ScreenCoordinate pixel = transform.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);
@@ -486,14 +396,14 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
// Calculate the zoom level.
double minScale = INFINITY;
if (width > 0 || height > 0) {
- double scaleX = double(getSize().width) / width;
- double scaleY = double(getSize().height) / height;
+ double scaleX = double(size.width) / width;
+ double scaleY = double(size.height) / height;
scaleX -= (padding.left() + padding.right()) / width;
scaleY -= (padding.top() + padding.bottom()) / height;
minScale = util::min(scaleX, scaleY);
}
- double zoom = getZoom() + util::log2(minScale);
- zoom = util::clamp(zoom, getMinZoom(), getMaxZoom());
+ double zoom = transform.getZoom() + util::log2(minScale);
+ zoom = util::clamp(zoom, transform.getState().getMinZoom(), transform.getState().getMaxZoom());
// Calculate the center point of a virtual bounds that is extended in all directions by padding.
ScreenCoordinate centerPixel = nePixel + swPixel;
@@ -511,11 +421,34 @@ CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const Ed
// CameraOptions origin is at the top-left corner.
centerPixel.y = viewportHeight - centerPixel.y;
- options.center = latLngForPixel(centerPixel);
+ options.center = transform.screenCoordinateToLatLng(centerPixel);
options.zoom = zoom;
return options;
}
+CameraOptions Map::cameraForLatLngs(const std::vector<LatLng>& latLngs, const EdgeInsets& padding, optional<double> bearing) const {
+ if(bearing) {
+ double angle = -*bearing * util::DEG2RAD; // Convert to radians
+ Transform transform(impl->transform.getState());
+ transform.setAngle(angle);
+ CameraOptions options = mbgl::cameraForLatLngs(latLngs, transform, padding);
+ options.angle = angle;
+ return options;
+ } else {
+ return mbgl::cameraForLatLngs(latLngs, impl->transform, padding);
+ }
+}
+
+CameraOptions Map::cameraForGeometry(const Geometry<double>& geometry, const EdgeInsets& padding, optional<double> bearing) const {
+
+ std::vector<LatLng> latLngs;
+ forEachPoint(geometry, [&](const Point<double>& pt) {
+ latLngs.push_back({ pt.y, pt.x });
+ });
+ return cameraForLatLngs(latLngs, padding, bearing);
+
+}
+
LatLngBounds Map::latLngBoundsForCamera(const CameraOptions& camera) const {
Transform shallow { impl->transform.getState() };
Size size = shallow.getState().getSize();
@@ -541,7 +474,7 @@ optional<LatLngBounds> Map::getLatLngBounds() const {
void Map::setLatLngBounds(optional<LatLngBounds> bounds) {
impl->cameraMutated = true;
impl->transform.setLatLngBounds(bounds);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setMinZoom(const double minZoom) {
@@ -592,7 +525,7 @@ double Map::getMaxPitch() const {
void Map::setSize(const Size size) {
impl->transform.resize(size);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
Size Map::getSize() const {
@@ -604,7 +537,7 @@ Size Map::getSize() const {
void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.rotateBy(first, second, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setBearing(double degrees, const AnimationOptions& animation) {
@@ -615,13 +548,13 @@ void Map::setBearing(double degrees, const AnimationOptions& animation) {
void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::setBearing(double degrees, const EdgeInsets& padding, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(-degrees * util::DEG2RAD, padding, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getBearing() const {
@@ -631,7 +564,7 @@ double Map::getBearing() const {
void Map::resetNorth(const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setAngle(0, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
#pragma mark - Pitch
@@ -644,7 +577,7 @@ void Map::setPitch(double pitch, const AnimationOptions& animation) {
void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const AnimationOptions& animation) {
impl->cameraMutated = true;
impl->transform.setPitch(pitch * util::DEG2RAD, anchor, animation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
double Map::getPitch() const {
@@ -655,7 +588,7 @@ double Map::getPitch() const {
void Map::setNorthOrientation(NorthOrientation orientation) {
impl->transform.setNorthOrientation(orientation);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
NorthOrientation Map::getNorthOrientation() const {
@@ -666,7 +599,7 @@ NorthOrientation Map::getNorthOrientation() const {
void Map::setConstrainMode(mbgl::ConstrainMode mode) {
impl->transform.setConstrainMode(mode);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
ConstrainMode Map::getConstrainMode() const {
@@ -677,13 +610,42 @@ ConstrainMode Map::getConstrainMode() const {
void Map::setViewportMode(mbgl::ViewportMode mode) {
impl->transform.setViewportMode(mode);
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
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 {
@@ -703,12 +665,10 @@ LatLng Map::latLngForPixel(const ScreenCoordinate& pixel) const {
void Map::addAnnotationImage(std::unique_ptr<style::Image> image) {
impl->annotationManager.addImage(std::move(image));
- impl->onUpdate(Update::AnnotationStyle);
}
void Map::removeAnnotationImage(const std::string& id) {
impl->annotationManager.removeImage(id);
- impl->onUpdate(Update::AnnotationStyle);
}
double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) {
@@ -716,79 +676,27 @@ double Map::getTopOffsetPixelsForAnnotationImage(const std::string& id) {
}
AnnotationID Map::addAnnotation(const Annotation& annotation) {
- auto result = impl->annotationManager.addAnnotation(annotation, getMaxZoom());
- impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData);
+ auto result = impl->annotationManager.addAnnotation(annotation);
+ impl->onUpdate();
return result;
}
void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) {
- impl->onUpdate(impl->annotationManager.updateAnnotation(id, annotation, getMaxZoom()));
+ if (impl->annotationManager.updateAnnotation(id, annotation)) {
+ impl->onUpdate();
+ }
}
void Map::removeAnnotation(AnnotationID annotation) {
impl->annotationManager.removeAnnotation(annotation);
- impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData);
-}
-
-#pragma mark - Feature query api
-
-std::vector<Feature> Map::queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options) {
- if (!impl->renderStyle) return {};
-
- return impl->renderStyle->queryRenderedFeatures(
- { point },
- impl->transform.getState(),
- options
- );
-}
-
-std::vector<Feature> Map::queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options) {
- if (!impl->renderStyle) return {};
-
- return impl->renderStyle->queryRenderedFeatures(
- {
- box.min,
- { box.max.x, box.min.y },
- box.max,
- { box.min.x, box.max.y },
- box.min
- },
- impl->transform.getState(),
- options
- );
-}
-
-std::vector<Feature> Map::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) {
- if (!impl->renderStyle) return {};
-
- const RenderSource* source = impl->renderStyle->getRenderSource(sourceID);
- if (!source) return {};
-
- return source->querySourceFeatures(options);
-}
-
-AnnotationIDs Map::queryPointAnnotations(const ScreenBox& box) {
- RenderedQueryOptions options;
- options.layerIDs = {{ AnnotationManager::PointLayerID }};
- auto features = queryRenderedFeatures(box, options);
- std::set<AnnotationID> set;
- for (auto &feature : features) {
- assert(feature.id);
- assert(feature.id->is<uint64_t>());
- assert(feature.id->get<uint64_t>() <= std::numeric_limits<AnnotationID>::max());
- set.insert(static_cast<AnnotationID>(feature.id->get<uint64_t>()));
- }
- AnnotationIDs ids;
- ids.reserve(set.size());
- std::move(set.begin(), set.end(), std::back_inserter(ids));
- return ids;
+ impl->onUpdate();
}
#pragma mark - Toggles
void Map::setDebug(MapDebugOptions debugOptions) {
impl->debugOptions = debugOptions;
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
void Map::cycleDebugOptions() {
@@ -812,65 +720,72 @@ void Map::cycleDebugOptions() {
else
impl->debugOptions = MapDebugOptions::TileBorders;
- impl->onUpdate(Update::Repaint);
+ impl->onUpdate();
}
MapDebugOptions Map::getDebug() const {
return impl->debugOptions;
}
-bool Map::isFullyLoaded() const {
- return impl->style->impl->isLoaded() && impl->renderStyle && impl->renderStyle->isLoaded();
+void Map::setPrefetchZoomDelta(uint8_t delta) {
+ impl->prefetchZoomDelta = delta;
}
-void Map::setSourceTileCacheSize(size_t size) {
- if (size != impl->sourceCacheSize) {
- impl->sourceCacheSize = size;
- if (!impl->renderStyle) return;
- impl->renderStyle->setSourceTileCacheSize(size);
- impl->backend.invalidate();
- }
+uint8_t Map::getPrefetchZoomDelta() const {
+ return impl->prefetchZoomDelta;
}
-void Map::onLowMemory() {
- if (impl->painter) {
- BackendScope guard(impl->backend);
- impl->painter->cleanup();
- }
- if (impl->renderStyle) {
- impl->renderStyle->onLowMemory();
- impl->backend.invalidate();
- }
+bool Map::isFullyLoaded() const {
+ return impl->style->impl->isLoaded() && impl->rendererFullyLoaded;
}
void Map::Impl::onSourceChanged(style::Source& source) {
observer.onSourceChanged(source);
}
-void Map::Impl::onUpdate(Update flags) {
- updateFlags |= flags;
- asyncInvalidate.send();
+void Map::Impl::onInvalidate() {
+ onUpdate();
}
-void Map::Impl::onInvalidate() {
- onUpdate(Update::Repaint);
+void Map::Impl::onUpdate() {
+ TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max();
+
+ transform.updateTransitions(timePoint);
+
+ UpdateParameters params = {
+ style->impl->isLoaded(),
+ mode,
+ pixelRatio,
+ debugOptions,
+ timePoint,
+ transform.getState(),
+ style->impl->getGlyphURL(),
+ style->impl->spriteLoaded,
+ style->impl->getTransitionOptions(),
+ style->impl->getLight()->impl,
+ style->impl->getImageImpls(),
+ style->impl->getSourceImpls(),
+ style->impl->getLayerImpls(),
+ annotationManager,
+ prefetchZoomDelta,
+ bool(stillImageRequest)
+ };
+
+ rendererFrontend.update(std::make_shared<UpdateParameters>(std::move(params)));
}
void Map::Impl::onStyleLoading() {
loading = true;
+ rendererFullyLoaded = false;
observer.onWillStartLoadingMap();
}
void Map::Impl::onStyleLoaded() {
if (!cameraMutated) {
- // Zoom first because it may constrain subsequent operations.
- map.setZoom(style->getDefaultZoom());
- map.setLatLng(style->getDefaultLatLng());
- map.setBearing(style->getDefaultBearing());
- map.setPitch(style->getDefaultPitch());
+ map.jumpTo(style->getDefaultCamera());
}
- onUpdate(Update::AnnotationStyle);
+ annotationManager.onStyleLoaded();
observer.onDidFinishLoadingStyle();
}
@@ -879,7 +794,7 @@ void Map::Impl::onStyleError(std::exception_ptr error) {
}
void Map::Impl::onResourceError(std::exception_ptr error) {
- if (mode == MapMode::Still && stillImageRequest) {
+ if (mode != MapMode::Continuous && stillImageRequest) {
auto request = std::move(stillImageRequest);
request->callback(error);
}
@@ -888,9 +803,6 @@ void Map::Impl::onResourceError(std::exception_ptr error) {
void Map::dumpDebugLogs() const {
Log::Info(Event::General, "--------------------------------------------------------------------------------");
impl->style->impl->dumpDebugLogs();
- if (impl->renderStyle) {
- impl->renderStyle->dumpDebugLogs();
- }
Log::Info(Event::General, "--------------------------------------------------------------------------------");
}
diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp
index 8d05bc0e91..105adf0400 100644
--- a/src/mbgl/map/transform.cpp
+++ b/src/mbgl/map/transform.cpp
@@ -1,6 +1,5 @@
#include <mbgl/map/camera.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/map/view.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/math.hpp>
@@ -168,7 +167,7 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
double angle = camera.angle.value_or(getAngle());
double pitch = camera.pitch.value_or(getPitch());
- if (std::isnan(zoom)) {
+ if (std::isnan(zoom) || state.size.isEmpty()) {
return;
}
@@ -293,6 +292,11 @@ void Transform::flyTo(const CameraOptions &camera, const AnimationOptions &anima
Point<double> framePoint = util::interpolate(startPoint, endPoint, us);
double frameZoom = startZoom + state.scaleZoom(1 / w(s));
+ // Zoom can be NaN if size is empty.
+ if (std::isnan(frameZoom)) {
+ frameZoom = zoom;
+ }
+
// Convert to geographic coordinates and set the new viewpoint.
LatLng frameLatLng = Projection::unproject(framePoint, startScale);
state.setLatLngZoom(frameLatLng, frameZoom);
@@ -523,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 bbf7e22b31..d79a65c61e 100644
--- a/src/mbgl/map/transform_state.cpp
+++ b/src/mbgl/map/transform_state.cpp
@@ -66,6 +66,15 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const {
matrix::translate(projMatrix, projMatrix, pixel_x() - size.width / 2.0f,
pixel_y() - size.height / 2.0f, 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()));
}
@@ -132,7 +141,7 @@ double TransformState::getZoom() const {
return scaleZoom(scale);
}
-int32_t TransformState::getIntegerZoom() const {
+uint8_t TransformState::getIntegerZoom() const {
return getZoom();
}
@@ -358,7 +367,7 @@ void TransformState::setLatLngZoom(const LatLng& latLng, double zoom) {
constrained = bounds->constrain(latLng);
}
- double newScale = zoomScale(zoom);
+ double newScale = util::clamp(zoomScale(zoom), min_scale, max_scale);
const double newWorldSize = newScale * util::tileSize;
Bc = newWorldSize / util::DEGREES_MAX;
Cc = newWorldSize / util::M2PI;
@@ -385,4 +394,16 @@ void TransformState::setScalePoint(const double newScale, const ScreenCoordinate
Cc = Projection::worldSize(scale) / util::M2PI;
}
+float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) const {
+ mat4 projectionMatrix;
+ getProjMatrix(projectionMatrix);
+ mat4 tileProjectionMatrix;
+ matrixFor(tileProjectionMatrix, tileID);
+ matrix::multiply(tileProjectionMatrix, projectionMatrix, tileProjectionMatrix);
+ vec4 tileCenter = {{util::tileSize / 2, util::tileSize / 2, 0, 1}};
+ vec4 projectedCenter;
+ matrix::transformMat4(projectedCenter, tileCenter, tileProjectionMatrix);
+ return projectedCenter[3];
+}
+
} // namespace mbgl
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index e6464aeacc..0dd6d5a15e 100644
--- a/src/mbgl/map/transform_state.hpp
+++ b/src/mbgl/map/transform_state.hpp
@@ -47,7 +47,7 @@ public:
// Zoom
double getZoom() const;
- int32_t getIntegerZoom() const;
+ uint8_t getIntegerZoom() const;
double getZoomFraction() const;
// Bounds
@@ -86,6 +86,8 @@ public:
return !size.isEmpty() && (scale >= min_scale && scale <= max_scale);
}
+ float getCameraToTileDistance(const UnwrappedTileID&) const;
+
private:
bool rotatedNorth() const;
void constrain(double& scale, double& x, double& y) const;
@@ -94,7 +96,7 @@ private:
// Limit the amount of zooming possible on the map.
double min_scale = std::pow(2, 0);
- double max_scale = std::pow(2, 20);
+ double max_scale = std::pow(2, util::DEFAULT_MAX_ZOOM);
double min_pitch = 0.0;
double max_pitch = util::PITCH_MAX;
@@ -132,6 +134,9 @@ private:
// `fov = 2 * arctan((height / 2) / (height * 1.5))`
double fov = 0.6435011087932844;
double pitch = 0.0;
+ double xSkew = 0.0;
+ double ySkew = 1.0;
+ bool axonometric = false;
// cache values for spherical mercator math
double Bc = Projection::worldSize(scale) / util::DEGREES_MAX;
diff --git a/src/mbgl/map/update.hpp b/src/mbgl/map/update.hpp
deleted file mode 100644
index 82d98284f5..0000000000
--- a/src/mbgl/map/update.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include <mbgl/util/traits.hpp>
-
-namespace mbgl {
-
-enum class Update {
- Nothing = 0,
- Repaint = 1 << 0,
- AnnotationStyle = 1 << 6,
- AnnotationData = 1 << 7
-};
-
-constexpr Update operator|(Update lhs, Update rhs) {
- return Update(mbgl::underlying_type(lhs) | mbgl::underlying_type(rhs));
-}
-
-constexpr Update& operator|=(Update& lhs, const Update& rhs) {
- return (lhs = lhs | rhs);
-}
-
-constexpr bool operator& (Update lhs, Update rhs) {
- return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/map/zoom_history.hpp b/src/mbgl/map/zoom_history.hpp
index 308846b1e3..7821499d72 100644
--- a/src/mbgl/map/zoom_history.hpp
+++ b/src/mbgl/map/zoom_history.hpp
@@ -8,33 +8,39 @@ namespace mbgl {
struct ZoomHistory {
float lastZoom;
+ float lastFloorZoom;
float lastIntegerZoom;
TimePoint lastIntegerZoomTime;
bool first = true;
bool update(float z, const TimePoint& now) {
+ constexpr TimePoint zero = TimePoint(Duration::zero());
+ const float floorZ = std::floor(z);
+
if (first) {
first = false;
- lastIntegerZoom = std::floor(z);
- lastIntegerZoomTime = TimePoint(Duration::zero());
+ lastIntegerZoom = floorZ;
+ lastIntegerZoomTime = zero;
+ lastZoom = z;
+ lastFloorZoom = floorZ;
+ return true;
+ }
+
+ if (lastFloorZoom > floorZ) {
+ lastIntegerZoom = floorZ + 1;
+ lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now;
+ } else if (lastFloorZoom < floorZ) {
+ lastIntegerZoom = floorZ;
+ lastIntegerZoomTime = now == Clock::time_point::max() ? zero : now;
+ }
+
+ if (z != lastZoom) {
lastZoom = z;
+ lastFloorZoom = floorZ;
return true;
- } else {
- if (std::floor(lastZoom) < std::floor(z)) {
- lastIntegerZoom = std::floor(z);
- lastIntegerZoomTime = now;
- } else if (std::floor(lastZoom) > std::floor(z)) {
- lastIntegerZoom = std::floor(z + 1);
- lastIntegerZoomTime = now;
- }
-
- if (z != lastZoom) {
- lastZoom = z;
- return true;
- }
-
- return false;
}
+
+ return false;
}
};
diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp
index 8f2751080f..b0582b0bc2 100644
--- a/src/mbgl/programs/attributes.hpp
+++ b/src/mbgl/programs/attributes.hpp
@@ -23,9 +23,14 @@ inline uint16_t packUint8Pair(T a, T b) {
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_pos);
MBGL_DEFINE_ATTRIBUTE(int16_t, 2, a_extrude);
MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_offset);
+MBGL_DEFINE_ATTRIBUTE(int16_t, 4, a_pos_normal);
+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 {
diff --git a/src/mbgl/programs/binary_program.cpp b/src/mbgl/programs/binary_program.cpp
index 09b9acc514..da629194b4 100644
--- a/src/mbgl/programs/binary_program.cpp
+++ b/src/mbgl/programs/binary_program.cpp
@@ -3,6 +3,7 @@
#include <protozero/pbf_reader.hpp>
#include <protozero/pbf_writer.hpp>
#include <utility>
+#include <stdexcept>
template <class Binding>
static std::pair<const std::string, Binding> parseBinding(protozero::pbf_reader&& pbf) {
@@ -97,7 +98,7 @@ std::string BinaryProgram::serialize() const {
return data;
}
-gl::AttributeLocation BinaryProgram::attributeLocation(const std::string& name) const {
+optional<gl::AttributeLocation> BinaryProgram::attributeLocation(const std::string& name) const {
for (const auto& pair : attributes) {
if (pair.first == name) {
return pair.second;
@@ -112,7 +113,7 @@ gl::UniformLocation BinaryProgram::uniformLocation(const std::string& name) cons
return pair.second;
}
}
- return {};
+ return -1;
}
} // namespace mbgl
diff --git a/src/mbgl/programs/binary_program.hpp b/src/mbgl/programs/binary_program.hpp
index b77cf1a510..8690f3fd6f 100644
--- a/src/mbgl/programs/binary_program.hpp
+++ b/src/mbgl/programs/binary_program.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/gl/types.hpp>
+#include <mbgl/util/optional.hpp>
#include <string>
#include <vector>
@@ -29,7 +30,8 @@ public:
const std::string& identifier() const {
return binaryIdentifier;
}
- gl::AttributeLocation attributeLocation(const std::string& name) const;
+
+ optional<gl::AttributeLocation> attributeLocation(const std::string& name) const;
gl::UniformLocation uniformLocation(const std::string& name) const;
private:
diff --git a/src/mbgl/programs/circle_program.hpp b/src/mbgl/programs/circle_program.hpp
index 8f056048b1..3590acbeef 100644
--- a/src/mbgl/programs/circle_program.hpp
+++ b/src/mbgl/programs/circle_program.hpp
@@ -21,7 +21,9 @@ class CircleProgram : public Program<
gl::Uniforms<
uniforms::u_matrix,
uniforms::u_scale_with_map,
- uniforms::u_extrude_scale>,
+ uniforms::u_extrude_scale,
+ uniforms::u_camera_to_center_distance,
+ uniforms::u_pitch_with_map>,
style::CirclePaintProperties>
{
public:
diff --git a/src/mbgl/programs/collision_box_program.cpp b/src/mbgl/programs/collision_box_program.cpp
index a3dc01ebe4..57107db75d 100644
--- a/src/mbgl/programs/collision_box_program.cpp
+++ b/src/mbgl/programs/collision_box_program.cpp
@@ -2,6 +2,6 @@
namespace mbgl {
-static_assert(sizeof(CollisionBoxProgram::LayoutVertex) == 10, "expected CollisionBoxVertex size");
+static_assert(sizeof(CollisionBoxProgram::LayoutVertex) == 14, "expected CollisionBoxVertex size");
} // namespace mbgl
diff --git a/src/mbgl/programs/collision_box_program.hpp b/src/mbgl/programs/collision_box_program.hpp
index 160fd42814..8d712a3df3 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,46 +12,170 @@
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_extrude,
- attributes::a_data<uint8_t, 2>>;
+ attributes::a_anchor_pos,
+ 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_extrude_scale,
+ uniforms::u_camera_to_center_distance>,
style::Properties<>>
{
public:
using Program::Program;
- static LayoutVertex vertex(Point<float> a, 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)
}},
{{
+ static_cast<int16_t>(anchor.x),
+ static_cast<int16_t>(anchor.y)
+ }},
+ {{
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_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<uint8_t>(maxzoom * 10),
- static_cast<uint8_t>(placementZoom * 10)
+ static_cast<int16_t>(anchor.x),
+ static_cast<int16_t>(anchor.y)
+ }},
+ {{
+ 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/line_program.cpp b/src/mbgl/programs/line_program.cpp
index db5c916d32..faf57ef19b 100644
--- a/src/mbgl/programs/line_program.cpp
+++ b/src/mbgl/programs/line_program.cpp
@@ -10,7 +10,7 @@ namespace mbgl {
using namespace style;
-static_assert(sizeof(LineLayoutVertex) == 8, "expected LineLayoutVertex size");
+static_assert(sizeof(LineLayoutVertex) == 12, "expected LineLayoutVertex size");
template <class Values, class...Args>
Values makeValues(const RenderLinePaintProperties::PossiblyEvaluated& properties,
diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp
index ed4a09bf10..da9964e623 100644
--- a/src/mbgl/programs/line_program.hpp
+++ b/src/mbgl/programs/line_program.hpp
@@ -30,7 +30,7 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_gl_units_to_pixels);
} // namespace uniforms
struct LineLayoutAttributes : gl::Attributes<
- attributes::a_pos,
+ attributes::a_pos_normal,
attributes::a_data<uint8_t, 4>>
{};
@@ -50,14 +50,17 @@ public:
/*
* @param p vertex position
* @param e extrude normal
- * @param t texture normal
+ * @param round whether the vertex uses a round line cap
+ * @param up whether the line normal points up or down
* @param dir direction of the line cap (-1/0/1)
*/
- static LayoutVertex layoutVertex(Point<int16_t> p, Point<double> e, Point<bool> t, int8_t dir, int32_t linesofar = 0) {
+ static LayoutVertex layoutVertex(Point<int16_t> p, Point<double> e, bool round, bool up, int8_t dir, int32_t linesofar = 0) {
return LayoutVertex {
{{
- static_cast<int16_t>((p.x * 2) | t.x),
- static_cast<int16_t>((p.y * 2) | t.y)
+ p.x,
+ p.y,
+ static_cast<int16_t>(round ? 1 : 0),
+ static_cast<int16_t>(up ? 1 : -1)
}},
{{
// add 128 to store a byte in an unsigned byte
diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp
index 3a38f30a86..bcdb270b9c 100644
--- a/src/mbgl/programs/program.hpp
+++ b/src/mbgl/programs/program.hpp
@@ -2,6 +2,7 @@
#include <mbgl/gl/program.hpp>
#include <mbgl/gl/features.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/binary_program.hpp>
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/program_parameters.hpp>
@@ -51,26 +52,40 @@ public:
gl::DepthMode depthMode,
gl::StencilMode stencilMode,
gl::ColorMode colorMode,
- UniformValues&& uniformValues,
+ const UniformValues& uniformValues,
const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
const gl::IndexBuffer<DrawMode>& indexBuffer,
- const gl::SegmentVector<Attributes>& segments,
+ const SegmentVector<Attributes>& segments,
const PaintPropertyBinders& paintPropertyBinders,
const typename PaintProperties::PossiblyEvaluated& currentProperties,
- float currentZoom) {
- program.draw(
- context,
- std::move(drawMode),
- std::move(depthMode),
- std::move(stencilMode),
- std::move(colorMode),
- uniformValues
- .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)),
- LayoutAttributes::bindings(layoutVertexBuffer)
- .concat(paintPropertyBinders.attributeBindings(currentProperties)),
- indexBuffer,
- segments
- );
+ float currentZoom,
+ const std::string& layerID) {
+ typename AllUniforms::Values allUniformValues = uniformValues
+ .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
+
+ typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer)
+ .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);
+ }
}
};
diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp
index 37ced32745..d769defaaf 100644
--- a/src/mbgl/programs/programs.hpp
+++ b/src/mbgl/programs/programs.hpp
@@ -32,7 +32,8 @@ public:
symbolIconSDF(context, programParameters),
symbolGlyph(context, programParameters),
debug(context, programParameters),
- collisionBox(context, programParameters) {
+ collisionBox(context, programParameters),
+ collisionCircle(context, programParameters) {
}
ProgramMap<CircleProgram> circle;
@@ -53,6 +54,7 @@ public:
DebugProgram debug;
CollisionBoxProgram collisionBox;
+ CollisionCircleProgram collisionCircle;
};
} // namespace mbgl
diff --git a/src/mbgl/programs/segment.hpp b/src/mbgl/programs/segment.hpp
new file mode 100644
index 0000000000..f729683ac9
--- /dev/null
+++ b/src/mbgl/programs/segment.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/vertex_array.hpp>
+
+#include <cstddef>
+#include <vector>
+#include <map>
+
+namespace mbgl {
+
+template <class Attributes>
+class Segment {
+public:
+ Segment(std::size_t vertexOffset_,
+ std::size_t indexOffset_,
+ std::size_t vertexLength_ = 0,
+ std::size_t indexLength_ = 0)
+ : vertexOffset(vertexOffset_),
+ indexOffset(indexOffset_),
+ vertexLength(vertexLength_),
+ indexLength(indexLength_) {}
+
+ const std::size_t vertexOffset;
+ const std::size_t indexOffset;
+
+ std::size_t vertexLength;
+ std::size_t indexLength;
+
+ // One VertexArray per layer ID. This minimizes rebinding in cases where
+ // several layers share buckets but have different sets of active attributes.
+ // This can happen:
+ // * when two layers have the same layout properties, but differing
+ // data-driven paint properties
+ // * when two fill layers have the same layout properties, but one
+ // uses fill-color and the other uses fill-pattern
+ mutable std::map<std::string, gl::VertexArray> vertexArrays;
+};
+
+template <class Attributes>
+using SegmentVector = std::vector<Segment<Attributes>>;
+
+} // namespace mbgl
diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp
index cdbd6b9713..84a7a53f1d 100644
--- a/src/mbgl/programs/symbol_program.cpp
+++ b/src/mbgl/programs/symbol_program.cpp
@@ -2,6 +2,8 @@
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/math/clamp.hpp>
@@ -32,8 +34,10 @@ Values makeValues(const bool isText,
const style::SymbolPropertyValues& values,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile& tile,
const TransformState& state,
+ const float symbolFadeChange,
Args&&... args) {
std::array<float, 2> extrudeScale;
@@ -45,18 +49,48 @@ Values makeValues(const bool isText,
pixelsToGLUnits[1] * state.getCameraToCenterDistance()
}};
}
+
+ const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom());
+ const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map;
+ const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map;
+
+ // Line label rotation happens in `updateLineLabels`
+ // Pitched point labels are automatically rotated by the labelPlaneMatrix projection
+ // Unpitched point labels need to have their rotation applied after projection
+ const bool rotateInShader = rotateWithMap && !pitchWithMap && !alongLine;
+
+ mat4 labelPlaneMatrix;
+ if (alongLine) {
+ // For labels that follow lines the first part of the projection is handled on the cpu.
+ // Pass an identity matrix because no transformation needs to be done in the vertex shader.
+ matrix::identity(labelPlaneMatrix);
+ } else {
+ labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
+ }
+
+ mat4 glCoordMatrix = getGlCoordMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
return Values {
uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate,
values.translateAnchor,
state) },
+ uniforms::u_label_plane_matrix::Value{labelPlaneMatrix},
+ uniforms::u_gl_coord_matrix::Value{ tile.translateVtxMatrix(glCoordMatrix,
+ values.translate,
+ values.translateAnchor,
+ state,
+ true) },
uniforms::u_extrude_scale::Value{ extrudeScale },
uniforms::u_texsize::Value{ texsize },
- uniforms::u_zoom::Value{ float(state.getZoom()) },
- uniforms::u_rotate_with_map::Value{ values.rotationAlignment == AlignmentType::Map },
uniforms::u_texture::Value{ 0 },
- uniforms::u_fadetexture::Value{ 1 },
+ uniforms::u_fade_change::Value{ symbolFadeChange },
uniforms::u_is_text::Value{ isText },
+ uniforms::u_camera_to_center_distance::Value{ state.getCameraToCenterDistance() },
+ uniforms::u_pitch::Value{ state.getPitch() },
+ uniforms::u_pitch_with_map::Value{ pitchWithMap },
+ uniforms::u_max_camera_distance::Value{ values.maxCameraDistance },
+ uniforms::u_rotate_symbol::Value{ rotateInShader },
+ uniforms::u_aspect_ratio::Value{ state.getSize().aspectRatio() },
std::forward<Args>(args)...
};
}
@@ -66,16 +100,20 @@ SymbolIconProgram::uniformValues(const bool isText,
const style::SymbolPropertyValues& values,
const Size& texsize,
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,
values,
texsize,
pixelsToGLUnits,
+ alongLine,
tile,
- state
+ state,
+ symbolFadeChange
);
}
@@ -85,26 +123,26 @@ typename SymbolSDFProgram<PaintProperties>::UniformValues SymbolSDFProgram<Paint
const style::SymbolPropertyValues& values,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile& tile,
const TransformState& state,
+ const float symbolFadeChange,
const SymbolSDFPart part)
{
const float gammaScale = (values.pitchAlignment == AlignmentType::Map
- ? std::cos(state.getPitch())
- : 1.0) * state.getCameraToCenterDistance();
+ ? std::cos(state.getPitch()) * state.getCameraToCenterDistance()
+ : 1.0);
return makeValues<SymbolSDFProgram<PaintProperties>::UniformValues>(
isText,
values,
texsize,
pixelsToGLUnits,
+ alongLine,
tile,
state,
+ symbolFadeChange,
uniforms::u_gamma_scale::Value{ gammaScale },
- uniforms::u_pitch::Value{ state.getPitch() },
- uniforms::u_bearing::Value{ -1.0f * state.getAngle() },
- uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) },
- uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map },
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 e7c428034b..a14afac702 100644
--- a/src/mbgl/programs/symbol_program.hpp
+++ b/src/mbgl/programs/symbol_program.hpp
@@ -7,6 +7,7 @@
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/shaders/symbol_icon.hpp>
#include <mbgl/shaders/symbol_sdf.hpp>
#include <mbgl/util/geometry.hpp>
@@ -29,11 +30,9 @@ class RenderTile;
class TransformState;
namespace uniforms {
-MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_with_map);
-MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map);
+MBGL_DEFINE_UNIFORM_MATRIX(double, 4, u_gl_coord_matrix);
+MBGL_DEFINE_UNIFORM_MATRIX(double, 4, u_label_plane_matrix);
MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture);
-MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale);
@@ -42,50 +41,66 @@ MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_zoom_constant);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_feature_constant);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_size_t);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_size);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_layout_size);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_max_camera_distance);
+MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_symbol);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio);
} // namespace uniforms
struct SymbolLayoutAttributes : gl::Attributes<
attributes::a_pos_offset,
attributes::a_data<uint16_t, 4>>
{
- static Vertex vertex(Point<float> a,
+ static Vertex vertex(Point<float> labelAnchor,
Point<float> o,
+ float glyphOffsetY,
uint16_t tx,
uint16_t ty,
- float minzoom,
- float maxzoom,
- float labelminzoom,
- uint8_t labelangle) {
+ const Range<float>& sizeData) {
return Vertex {
// combining pos and offset to reduce number of vertex attributes passed to shader (8 max for some devices)
{{
- static_cast<int16_t>(a.x),
- static_cast<int16_t>(a.y),
+ 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 * 64))
+ static_cast<int16_t>(::round((o.y + glyphOffsetY) * 64))
}},
{{
tx,
ty,
- mbgl::attributes::packUint8Pair(
- static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160
- static_cast<uint8_t>(labelangle)
- ),
- mbgl::attributes::packUint8Pair(
- static_cast<uint8_t>(minzoom * 10),
- static_cast<uint8_t>(::fmin(maxzoom, 25) * 10)
- )
+ static_cast<uint16_t>(sizeData.min * 10),
+ static_cast<uint16_t>(sizeData.max * 10)
}}
};
}
};
-
-class SymbolSizeAttributes : public gl::Attributes<attributes::a_size> {
-public:
- using Attribute = attributes::a_size::Type;
+
+struct SymbolDynamicLayoutAttributes : gl::Attributes<attributes::a_projected_pos> {
+ static Vertex vertex(Point<float> anchorPoint, float labelAngle) {
+ return Vertex {
+ {{
+ anchorPoint.x,
+ anchorPoint.y,
+ 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;
+ bool isFeatureConstant;
+ float sizeT;
+ float size;
+ float layoutSize;
+};
// Mimic the PaintPropertyBinder technique specifically for the {text,icon}-size layout properties
// in order to provide a 'custom' scheme for encoding the necessary attribute data. As with
// PaintPropertyBinder, SymbolSizeBinder is an abstract class whose implementations handle the
@@ -98,37 +113,27 @@ public:
uniforms::u_is_size_zoom_constant,
uniforms::u_is_size_feature_constant,
uniforms::u_size_t,
- uniforms::u_size,
- uniforms::u_layout_size>;
+ uniforms::u_size>;
using UniformValues = Uniforms::Values;
static std::unique_ptr<SymbolSizeBinder> create(const float tileZoom,
const style::DataDrivenPropertyValue<float>& sizeProperty,
const float defaultValue);
- virtual SymbolSizeAttributes::Bindings attributeBindings() const = 0;
- virtual void populateVertexVector(const GeometryTileFeature& feature) = 0;
- virtual UniformValues uniformValues(float currentZoom) const = 0;
- virtual void upload(gl::Context&) = 0;
-};
+ virtual Range<float> getVertexSizeData(const GeometryTileFeature& feature) = 0;
+ virtual ZoomEvaluatedSize evaluateForZoom(float currentZoom) const = 0;
-// 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--;
+ UniformValues uniformValues(float currentZoom) const {
+ const ZoomEvaluatedSize u = evaluateForZoom(currentZoom);
+ return UniformValues {
+ uniforms::u_is_size_zoom_constant::Value{ u.isZoomConstant },
+ uniforms::u_is_size_feature_constant::Value{ u.isFeatureConstant},
+ uniforms::u_size_t::Value{ u.sizeT },
+ uniforms::u_size::Value{ u.size }
+ };
}
- 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:
@@ -139,30 +144,18 @@ 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) }
);
}
- SymbolSizeAttributes::Bindings attributeBindings() const override {
- return SymbolSizeAttributes::Bindings { gl::DisabledAttribute() };
- }
-
- void upload(gl::Context&) override {}
- void populateVertexVector(const GeometryTileFeature&) override {};
+ Range<float> getVertexSizeData(const GeometryTileFeature&) override { return { 0.0f, 0.0f }; };
- UniformValues uniformValues(float currentZoom) const override {
+ ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override {
float size = layoutSize;
bool isZoomConstant = !(coveringRanges || function);
if (coveringRanges) {
@@ -174,28 +167,20 @@ 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);
} else if (function) {
size = function->evaluate(currentZoom);
}
-
- return UniformValues {
- uniforms::u_is_size_zoom_constant::Value{ isZoomConstant },
- uniforms::u_is_size_feature_constant::Value{ true },
- uniforms::u_size_t::Value{ 0.0f }, // unused
- uniforms::u_size::Value{ size },
- uniforms::u_layout_size::Value{ layoutSize }
- };
+
+ const float unused = 0.0f;
+ return { isZoomConstant, true, unused, size, layoutSize };
}
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;
};
@@ -210,104 +195,51 @@ public:
defaultValue(defaultValue_) {
}
- SymbolSizeAttributes::Bindings attributeBindings() const override {
- return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::binding(*buffer, 0, 1) };
- }
-
- void populateVertexVector(const GeometryTileFeature& feature) override {
- const auto sizeVertex = Vertex {
- {{
- static_cast<uint16_t>(function.evaluate(feature, defaultValue) * 10)
- }}
- };
-
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
+ Range<float> getVertexSizeData(const GeometryTileFeature& feature) override {
+ const float size = function.evaluate(feature, defaultValue);
+ return { size, size };
};
- UniformValues uniformValues(float) const override {
- return UniformValues {
- uniforms::u_is_size_zoom_constant::Value{ true },
- uniforms::u_is_size_feature_constant::Value{ false },
- uniforms::u_size_t::Value{ 0.0f }, // unused
- uniforms::u_size::Value{ 0.0f }, // unused
- uniforms::u_layout_size::Value{ 0.0f } // unused
- };
- }
-
- void upload(gl::Context& context) override {
- buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) };
+ ZoomEvaluatedSize evaluateForZoom(float) const override {
+ const float unused = 0.0f;
+ return { true, false, unused, unused, unused };
}
- const style::SourceFunction<float>& function;
+ style::SourceFunction<float> function;
const float defaultValue;
-
- VertexVector vertices;
- optional<VertexBuffer> buffer;
};
class CompositeFunctionSymbolSizeBinder final : public SymbolSizeBinder {
public:
- using Vertex = SymbolSizeAttributes::Vertex;
- using VertexVector = gl::VertexVector<Vertex>;
- using VertexBuffer = gl::VertexBuffer<Vertex>;
CompositeFunctionSymbolSizeBinder(const float tileZoom, const style::CompositeFunction<float>& function_, const float defaultValue_)
: 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))
{}
- SymbolSizeAttributes::Bindings attributeBindings() const override {
- return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::binding(*buffer, 0) };
- }
-
- void populateVertexVector(const GeometryTileFeature& feature) override {
- const auto sizeVertex = Vertex {
- {{
- static_cast<uint16_t>(function.evaluate(coveringZoomStops.min, feature, defaultValue) * 10),
- static_cast<uint16_t>(function.evaluate(coveringZoomStops.max, feature, defaultValue) * 10),
- static_cast<uint16_t>(function.evaluate(layoutZoom, feature, defaultValue) * 10)
- }}
+ Range<float> getVertexSizeData(const GeometryTileFeature& feature) override {
+ return {
+ function.evaluate(coveringZoomStops.min, feature, defaultValue),
+ function.evaluate(coveringZoomStops.max, feature, defaultValue)
};
-
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
- vertices.emplace_back(sizeVertex);
};
- UniformValues uniformValues(float currentZoom) const override {
+ ZoomEvaluatedSize evaluateForZoom(float currentZoom) const override {
float sizeInterpolationT = util::clamp(
- util::interpolationFactor(1.0f, coveringZoomStops, currentZoom),
+ function.interpolationFactor(coveringZoomStops, currentZoom),
0.0f, 1.0f
);
- return UniformValues {
- uniforms::u_is_size_zoom_constant::Value{ false },
- uniforms::u_is_size_feature_constant::Value{ false },
- uniforms::u_size_t::Value{ sizeInterpolationT },
- uniforms::u_size::Value{ 0.0f }, // unused
- uniforms::u_layout_size::Value{ 0.0f } // unused
- };
- }
-
- void upload(gl::Context& context) override {
- buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) };
+ const float unused = 0.0f;
+ return { false, false, sizeInterpolationT, unused, unused };
}
- const style::CompositeFunction<float>& function;
+ style::CompositeFunction<float> function;
const float defaultValue;
float layoutZoom;
Range<float> coveringZoomStops;
-
- VertexVector vertices;
- optional<VertexBuffer> buffer;
};
@@ -321,7 +253,7 @@ public:
using LayoutAttributes = LayoutAttrs;
using LayoutVertex = typename LayoutAttributes::Vertex;
- using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolSizeAttributes>;
+ using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, gl::ConcatenateAttributes<SymbolDynamicLayoutAttributes, SymbolOpacityAttributes>>;
using PaintProperties = PaintProps;
using PaintPropertyBinders = typename PaintProperties::Binders;
@@ -352,29 +284,49 @@ public:
gl::DepthMode depthMode,
gl::StencilMode stencilMode,
gl::ColorMode colorMode,
- UniformValues&& uniformValues,
+ 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 gl::SegmentVector<Attributes>& segments,
+ const SegmentVector<Attributes>& segments,
const PaintPropertyBinders& paintPropertyBinders,
const typename PaintProperties::PossiblyEvaluated& currentProperties,
- float currentZoom) {
- program.draw(
- context,
- std::move(drawMode),
- std::move(depthMode),
- std::move(stencilMode),
- std::move(colorMode),
- uniformValues
- .concat(symbolSizeBinder.uniformValues(currentZoom))
- .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)),
- LayoutAttributes::bindings(layoutVertexBuffer)
- .concat(symbolSizeBinder.attributeBindings())
- .concat(paintPropertyBinders.attributeBindings(currentProperties)),
- indexBuffer,
- segments
- );
+ float currentZoom,
+ const std::string& layerID) {
+ typename AllUniforms::Values allUniformValues = uniformValues
+ .concat(symbolSizeBinder.uniformValues(currentZoom))
+ .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties));
+
+ 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);
+
+ 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);
+ }
}
};
@@ -384,13 +336,19 @@ class SymbolIconProgram : public SymbolProgram<
SymbolLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
+ uniforms::u_label_plane_matrix,
+ uniforms::u_gl_coord_matrix,
uniforms::u_extrude_scale,
uniforms::u_texsize,
- uniforms::u_zoom,
- uniforms::u_rotate_with_map,
uniforms::u_texture,
- uniforms::u_fadetexture,
- uniforms::u_is_text>,
+ uniforms::u_fade_change,
+ uniforms::u_is_text,
+ uniforms::u_camera_to_center_distance,
+ uniforms::u_pitch,
+ uniforms::u_pitch_with_map,
+ uniforms::u_max_camera_distance,
+ uniforms::u_rotate_symbol,
+ uniforms::u_aspect_ratio>,
style::IconPaintProperties>
{
public:
@@ -400,8 +358,10 @@ public:
const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ const bool alongLine,
const RenderTile&,
- const TransformState&);
+ const TransformState&,
+ const float symbolFadeChange);
};
enum class SymbolSDFPart {
@@ -416,18 +376,20 @@ class SymbolSDFProgram : public SymbolProgram<
SymbolLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
+ uniforms::u_label_plane_matrix,
+ uniforms::u_gl_coord_matrix,
uniforms::u_extrude_scale,
uniforms::u_texsize,
- uniforms::u_zoom,
- uniforms::u_rotate_with_map,
uniforms::u_texture,
- uniforms::u_fadetexture,
+ uniforms::u_fade_change,
uniforms::u_is_text,
- uniforms::u_gamma_scale,
+ uniforms::u_camera_to_center_distance,
uniforms::u_pitch,
- uniforms::u_bearing,
- uniforms::u_aspect_ratio,
uniforms::u_pitch_with_map,
+ uniforms::u_max_camera_distance,
+ uniforms::u_rotate_symbol,
+ uniforms::u_aspect_ratio,
+ uniforms::u_gamma_scale,
uniforms::u_is_halo>,
PaintProperties>
{
@@ -437,18 +399,20 @@ public:
SymbolLayoutAttributes,
gl::Uniforms<
uniforms::u_matrix,
+ uniforms::u_label_plane_matrix,
+ uniforms::u_gl_coord_matrix,
uniforms::u_extrude_scale,
uniforms::u_texsize,
- uniforms::u_zoom,
- uniforms::u_rotate_with_map,
uniforms::u_texture,
- uniforms::u_fadetexture,
+ uniforms::u_fade_change,
uniforms::u_is_text,
- uniforms::u_gamma_scale,
+ uniforms::u_camera_to_center_distance,
uniforms::u_pitch,
- uniforms::u_bearing,
+ uniforms::u_pitch_with_map,
+ uniforms::u_max_camera_distance,
+ uniforms::u_rotate_symbol,
uniforms::u_aspect_ratio,
- uniforms::u_pitch_with_map,
+ uniforms::u_gamma_scale,
uniforms::u_is_halo>,
PaintProperties>;
@@ -462,8 +426,10 @@ public:
const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
+ 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 f1b2c2fb54..184f42e504 100644
--- a/src/mbgl/programs/uniforms.hpp
+++ b/src/mbgl/programs/uniforms.hpp
@@ -14,6 +14,7 @@ MBGL_DEFINE_UNIFORM_SCALAR(Color, u_color);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_blur);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_zoom);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_collision_y_stretch);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_pitch);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_bearing);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_radius);
@@ -33,6 +34,9 @@ MBGL_DEFINE_UNIFORM_SCALAR(float, u_gapwidth);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_offset);
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_VECTOR(float, 2, u_extrude_scale);
@@ -47,6 +51,7 @@ MBGL_DEFINE_UNIFORM_VECTOR(float, 2, u_pixel_coord_lower);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_mix);
MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_image);
+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);
diff --git a/src/mbgl/renderer/backend_scope.cpp b/src/mbgl/renderer/backend_scope.cpp
new file mode 100644
index 0000000000..fafeaabb39
--- /dev/null
+++ b/src/mbgl/renderer/backend_scope.cpp
@@ -0,0 +1,66 @@
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/util/thread_local.hpp>
+
+#include <cassert>
+
+namespace mbgl {
+
+static util::ThreadLocal<BackendScope> currentScope;
+
+BackendScope::BackendScope(RendererBackend& backend_, ScopeType scopeType_)
+ : priorScope(currentScope.get()),
+ nextScope(nullptr),
+ backend(backend_),
+ scopeType(scopeType_) {
+ if (priorScope) {
+ assert(priorScope->nextScope == nullptr);
+ priorScope->nextScope = this;
+ priorScope->deactivate();
+ }
+
+ activate();
+
+ currentScope.set(this);
+}
+
+BackendScope::~BackendScope() {
+ assert(nextScope == nullptr);
+ deactivate();
+
+ if (priorScope) {
+ priorScope->activate();
+ currentScope.set(priorScope);
+ assert(priorScope->nextScope == this);
+ priorScope->nextScope = nullptr;
+ } else {
+ currentScope.set(nullptr);
+ }
+}
+
+void BackendScope::activate() {
+ if (scopeType == ScopeType::Explicit &&
+ !(priorScope && this->backend == priorScope->backend) &&
+ !(nextScope && this->backend == nextScope->backend)) {
+ // Only activate when set to Explicit and
+ // only once per RenderBackend
+ backend.activate();
+ activated = true;
+ }
+}
+
+void BackendScope::deactivate() {
+ if (activated &&
+ !(nextScope && this->backend == nextScope->backend)) {
+ // Only deactivate when set to Explicit and
+ // only once per RenderBackend
+ backend.deactivate();
+ activated = false;
+ }
+}
+
+bool BackendScope::exists() {
+ return currentScope.get();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp
index 6c391cf014..9af511a03e 100644
--- a/src/mbgl/renderer/bucket.hpp
+++ b/src/mbgl/renderer/bucket.hpp
@@ -1,33 +1,26 @@
#pragma once
-#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/renderer/render_layer.hpp>
#include <atomic>
-#include <string>
-#include <unordered_map>
namespace mbgl {
-class Painter;
-class PaintParameters;
-class RenderTile;
-
namespace gl {
class Context;
} // namespace gl
-namespace style {
-class Layer;
-} // namespace style
+class RenderLayer;
class Bucket : private util::noncopyable {
public:
Bucket() = default;
virtual ~Bucket() = default;
+ // Feature geometries are also used to populate the feature index.
+ // Obtaining these is a costly operation, so we do it only once, and
+ // pass-by-const-ref the geometries as a second parameter.
virtual void addFeature(const GeometryTileFeature&,
const GeometryCollection&) {};
@@ -35,10 +28,6 @@ public:
// this only happens once when the bucket is being rendered for the first time.
virtual void upload(gl::Context&) = 0;
- // Every time this bucket is getting rendered, this function is called. This happens either
- // once or twice (for Opaque and Transparent render passes).
- virtual void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) = 0;
-
virtual bool hasData() const = 0;
virtual float getQueryRadius(const RenderLayer&) const {
@@ -46,7 +35,7 @@ public:
};
bool needsUpload() const {
- return !uploaded;
+ return hasData() && !uploaded;
}
protected:
diff --git a/src/mbgl/renderer/buckets/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp
index 8b5743d500..d23f0861f4 100644
--- a/src/mbgl/renderer/buckets/circle_bucket.cpp
+++ b/src/mbgl/renderer/buckets/circle_bucket.cpp
@@ -1,6 +1,5 @@
#include <mbgl/renderer/buckets/circle_bucket.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/programs/circle_program.hpp>
#include <mbgl/style/layers/circle_layer_impl.hpp>
#include <mbgl/renderer/layers/render_circle_layer.hpp>
@@ -34,13 +33,6 @@ void CircleBucket::upload(gl::Context& context) {
uploaded = true;
}
-void CircleBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderCircle(parameters, *this, *layer.as<RenderCircleLayer>(), tile);
-}
-
bool CircleBucket::hasData() const {
return !segments.empty();
}
@@ -57,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()) {
diff --git a/src/mbgl/renderer/buckets/circle_bucket.hpp b/src/mbgl/renderer/buckets/circle_bucket.hpp
index b048fd7675..78b6351bcb 100644
--- a/src/mbgl/renderer/buckets/circle_bucket.hpp
+++ b/src/mbgl/renderer/buckets/circle_bucket.hpp
@@ -5,7 +5,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/circle_program.hpp>
#include <mbgl/style/layers/circle_layer_properties.hpp>
@@ -23,13 +23,11 @@ public:
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
-
float getQueryRadius(const RenderLayer&) const override;
gl::VertexVector<CircleLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<CircleAttributes> segments;
+ SegmentVector<CircleAttributes> segments;
optional<gl::VertexBuffer<CircleLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
diff --git a/src/mbgl/renderer/buckets/debug_bucket.cpp b/src/mbgl/renderer/buckets/debug_bucket.cpp
index acfe15d2fb..53c751c443 100644
--- a/src/mbgl/renderer/buckets/debug_bucket.cpp
+++ b/src/mbgl/renderer/buckets/debug_bucket.cpp
@@ -1,7 +1,7 @@
#include <mbgl/renderer/buckets/debug_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/geometry/debug_font_data.hpp>
+#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/string.hpp>
#include <cmath>
diff --git a/src/mbgl/renderer/buckets/debug_bucket.hpp b/src/mbgl/renderer/buckets/debug_bucket.hpp
index 756e58a6de..fc3128e944 100644
--- a/src/mbgl/renderer/buckets/debug_bucket.hpp
+++ b/src/mbgl/renderer/buckets/debug_bucket.hpp
@@ -33,7 +33,7 @@ public:
const optional<Timestamp> expires;
const MapDebugOptions debugMode;
- gl::SegmentVector<DebugAttributes> segments;
+ SegmentVector<DebugAttributes> segments;
optional<gl::VertexBuffer<DebugLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Lines>> indexBuffer;
};
diff --git a/src/mbgl/renderer/buckets/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp
index 042d7b7506..110db887a1 100644
--- a/src/mbgl/renderer/buckets/fill_bucket.cpp
+++ b/src/mbgl/renderer/buckets/fill_bucket.cpp
@@ -1,5 +1,4 @@
#include <mbgl/renderer/buckets/fill_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
@@ -121,13 +120,6 @@ void FillBucket::upload(gl::Context& context) {
uploaded = true;
}
-void FillBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderFill(parameters, *this, *layer.as<RenderFillLayer>(), tile);
-}
-
bool FillBucket::hasData() const {
return !triangleSegments.empty() || !lineSegments.empty();
}
diff --git a/src/mbgl/renderer/buckets/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp
index 421d8b332b..a50e1971f5 100644
--- a/src/mbgl/renderer/buckets/fill_bucket.hpp
+++ b/src/mbgl/renderer/buckets/fill_bucket.hpp
@@ -4,7 +4,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/style/layers/fill_layer_properties.hpp>
@@ -23,15 +23,14 @@ public:
bool hasData() const override;
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
float getQueryRadius(const RenderLayer&) const override;
gl::VertexVector<FillLayoutVertex> vertices;
gl::IndexVector<gl::Lines> lines;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<FillAttributes> lineSegments;
- gl::SegmentVector<FillAttributes> triangleSegments;
+ SegmentVector<FillAttributes> lineSegments;
+ SegmentVector<FillAttributes> triangleSegments;
optional<gl::VertexBuffer<FillLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Lines>> lineIndexBuffer;
diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
index f61f1d1549..5e2c937091 100644
--- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
+++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
@@ -1,5 +1,4 @@
#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/programs/fill_extrusion_program.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
@@ -102,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));
@@ -154,13 +157,6 @@ void FillExtrusionBucket::upload(gl::Context& context) {
uploaded = true;
}
-void FillExtrusionBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderFillExtrusion(parameters, *this, *layer.as<RenderFillExtrusionLayer>(), tile);
-}
-
bool FillExtrusionBucket::hasData() const {
return !triangleSegments.empty();
}
diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
index c54805d743..d57265ab16 100644
--- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
+++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp
@@ -4,7 +4,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/fill_extrusion_program.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
@@ -21,13 +21,12 @@ public:
bool hasData() const override;
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
float getQueryRadius(const RenderLayer&) const override;
gl::VertexVector<FillExtrusionLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<FillExtrusionAttributes> triangleSegments;
+ SegmentVector<FillExtrusionAttributes> triangleSegments;
optional<gl::VertexBuffer<FillExtrusionLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp
index 3af3cd63d3..a96518df38 100644
--- a/src/mbgl/renderer/buckets/line_bucket.cpp
+++ b/src/mbgl/renderer/buckets/line_bucket.cpp
@@ -1,5 +1,4 @@
#include <mbgl/renderer/buckets/line_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/layers/render_line_layer.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/line_layer_impl.hpp>
@@ -16,7 +15,8 @@ LineBucket::LineBucket(const BucketParameters& parameters,
const std::vector<const RenderLayer*>& layers,
const style::LineLayoutProperties::Unevaluated& layout_)
: layout(layout_.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ))),
- overscaling(parameters.tileID.overscaleFactor()) {
+ overscaling(parameters.tileID.overscaleFactor()),
+ zoom(parameters.tileID.overscaledZ) {
for (const auto& layer : layers) {
paintPropertyBinders.emplace(
std::piecewise_construct,
@@ -30,7 +30,7 @@ LineBucket::LineBucket(const BucketParameters& parameters,
void LineBucket::addFeature(const GeometryTileFeature& feature,
const GeometryCollection& geometryCollection) {
for (auto& line : geometryCollection) {
- addGeometry(line, feature.getType());
+ addGeometry(line, feature);
}
for (auto& pair : paintPropertyBinders) {
@@ -63,7 +63,8 @@ const float LINE_DISTANCE_SCALE = 1.0 / 2.0;
// The maximum line distance, in tile units, that fits in the buffer.
const float MAX_LINE_DISTANCE = std::pow(2, LINE_DISTANCE_BUFFER_BITS) / LINE_DISTANCE_SCALE;
-void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType type) {
+void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const GeometryTileFeature& feature) {
+ const FeatureType type = feature.getType();
const std::size_t len = [&coordinates] {
std::size_t l = coordinates.size();
// If the line has duplicate vertices at the end, adjust length to remove them.
@@ -87,7 +88,9 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType
return;
}
- const float miterLimit = layout.get<LineJoin>() == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>());
+ const LineJoinType joinType = layout.evaluate<LineJoin>(zoom, feature);
+
+ const float miterLimit = joinType == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>());
const double sharpCornerOffset = SHARP_CORNER_OFFSET * (float(util::EXTENT) / (util::tileSize * overscaling));
@@ -194,7 +197,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType
// The join if a middle vertex, otherwise the cap
const bool middleVertex = prevCoordinate && nextCoordinate;
- LineJoinType currentJoin = layout.get<LineJoin>();
+ LineJoinType currentJoin = joinType;
const LineCapType currentCap = nextCoordinate ? beginCap : endCap;
if (middleVertex) {
@@ -398,7 +401,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate,
Point<double> extrude = normal;
if (endLeft)
extrude = extrude - (util::perp(normal) * endLeft);
- vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, false }, endLeft, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, false, endLeft, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
@@ -409,7 +412,7 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate,
extrude = normal * -1.0;
if (endRight)
extrude = extrude - (util::perp(normal) * endRight);
- vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, true }, -endRight, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, true, -endRight, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
@@ -434,7 +437,7 @@ void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex,
std::size_t startVertex,
std::vector<TriangleElement>& triangleStore) {
Point<double> flippedExtrude = extrude * (lineTurnsLeft ? -1.0 : 1.0);
- vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, { false, lineTurnsLeft }, 0, distance * LINE_DISTANCE_SCALE));
+ vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, false, lineTurnsLeft, 0, distance * LINE_DISTANCE_SCALE));
e3 = vertices.vertexSize() - 1 - startVertex;
if (e1 >= 0 && e2 >= 0) {
triangleStore.emplace_back(e1, e2, e3);
@@ -458,13 +461,6 @@ void LineBucket::upload(gl::Context& context) {
uploaded = true;
}
-void LineBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderLine(parameters, *this, *layer.as<RenderLineLayer>(), tile);
-}
-
bool LineBucket::hasData() const {
return !segments.empty();
}
diff --git a/src/mbgl/renderer/buckets/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp
index 34d8935953..4fb77c377e 100644
--- a/src/mbgl/renderer/buckets/line_bucket.hpp
+++ b/src/mbgl/renderer/buckets/line_bucket.hpp
@@ -4,7 +4,7 @@
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/line_program.hpp>
#include <mbgl/style/layers/line_layer_properties.hpp>
@@ -26,7 +26,6 @@ public:
bool hasData() const override;
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
float getQueryRadius(const RenderLayer&) const override;
@@ -34,7 +33,7 @@ public:
gl::VertexVector<LineLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<LineAttributes> segments;
+ SegmentVector<LineAttributes> segments;
optional<gl::VertexBuffer<LineLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
@@ -42,7 +41,7 @@ public:
std::map<std::string, LineProgram::PaintPropertyBinders> paintPropertyBinders;
private:
- void addGeometry(const GeometryCoordinates&, FeatureType);
+ void addGeometry(const GeometryCoordinates&, const GeometryTileFeature&);
struct TriangleElement {
TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {}
@@ -60,6 +59,7 @@ private:
std::ptrdiff_t e3;
const uint32_t overscaling;
+ const float zoom;
float getLineWidth(const RenderLineLayer& layer) const;
};
diff --git a/src/mbgl/renderer/buckets/raster_bucket.cpp b/src/mbgl/renderer/buckets/raster_bucket.cpp
index 49ec0065c3..a66dd42d74 100644
--- a/src/mbgl/renderer/buckets/raster_bucket.cpp
+++ b/src/mbgl/renderer/buckets/raster_bucket.cpp
@@ -1,22 +1,28 @@
#include <mbgl/renderer/buckets/raster_bucket.hpp>
#include <mbgl/renderer/layers/render_raster_layer.hpp>
#include <mbgl/programs/raster_program.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/gl/context.hpp>
-#include <mbgl/renderer/render_tile.hpp>
namespace mbgl {
using namespace style;
-RasterBucket::RasterBucket(UnassociatedImage&& image_) : image(std::move(image_)) {
+RasterBucket::RasterBucket(PremultipliedImage&& image_) {
+ image = std::make_shared<PremultipliedImage>(std::move(image_));
+}
+
+RasterBucket::RasterBucket(std::shared_ptr<PremultipliedImage> image_): image(image_) {
+
}
void RasterBucket::upload(gl::Context& context) {
+ if (!hasData()) {
+ return;
+ }
if (!texture) {
- texture = context.createTexture(image);
+ texture = context.createTexture(*image);
}
- if (!vertices.empty()) {
+ if (!segments.empty()) {
vertexBuffer = context.createVertexBuffer(std::move(vertices));
indexBuffer = context.createIndexBuffer(std::move(indices));
}
@@ -32,22 +38,73 @@ void RasterBucket::clear() {
uploaded = false;
}
-void RasterBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), tile.matrix, false);
+
+void RasterBucket::setImage(std::shared_ptr<PremultipliedImage> image_) {
+ image = std::move(image_);
+ texture = {};
+ uploaded = false;
}
-void RasterBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const mat4& matrix) {
- painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), matrix, true);
+void RasterBucket::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(
+ RasterProgram::layoutVertex({ tlVertex.x, tlVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(tlVertex.y) }));
+ vertices.emplace_back(
+ RasterProgram::layoutVertex({ brVertex.x, tlVertex.y }, { static_cast<uint16_t>(brVertex.x), static_cast<uint16_t>(tlVertex.y) }));
+ vertices.emplace_back(
+ RasterProgram::layoutVertex({ tlVertex.x, brVertex.y }, { static_cast<uint16_t>(tlVertex.x), static_cast<uint16_t>(brVertex.y) }));
+ vertices.emplace_back(
+ RasterProgram::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 RasterBucket::hasData() const {
- return true;
+ return !!image;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/raster_bucket.hpp b/src/mbgl/renderer/buckets/raster_bucket.hpp
index b5cf7997d5..3800eadec8 100644
--- a/src/mbgl/renderer/buckets/raster_bucket.hpp
+++ b/src/mbgl/renderer/buckets/raster_bucket.hpp
@@ -5,6 +5,7 @@
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/programs/raster_program.hpp>
#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/tile_mask.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/optional.hpp>
@@ -13,25 +14,25 @@ namespace mbgl {
class RasterBucket : public Bucket {
public:
- RasterBucket(UnassociatedImage&&);
+ RasterBucket(PremultipliedImage&&);
+ RasterBucket(std::shared_ptr<PremultipliedImage>);
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
- void render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const mat4& matrix);
bool hasData() const override;
void clear();
- UnassociatedImage image;
+ void setImage(std::shared_ptr<PremultipliedImage>);
+ void setMask(TileMask&&);
+
+ std::shared_ptr<PremultipliedImage> image;
optional<gl::Texture> texture;
+ TileMask mask{ { 0, 0, 0 } };
// Bucket specific vertices are used for Image Sources only
// Raster Tile Sources use the default buffers from Painter
gl::VertexVector<RasterLayoutVertex> vertices;
gl::IndexVector<gl::Triangles> indices;
- gl::SegmentVector<RasterAttributes> segments;
+ SegmentVector<RasterAttributes> segments;
optional<gl::VertexBuffer<RasterLayoutVertex>> vertexBuffer;
optional<gl::IndexBuffer<gl::Triangles>> indexBuffer;
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp
index cbddade899..60e8a0b504 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -1,5 +1,4 @@
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
@@ -17,10 +16,14 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons_,
- bool iconsNeedLinear_)
+ bool iconsNeedLinear_,
+ bool sortFeaturesByY_,
+ const std::vector<SymbolInstance>&& symbolInstances_)
: layout(std::move(layout_)),
sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
+ sortFeaturesByY(sortFeaturesByY_),
+ symbolInstances(std::move(symbolInstances_)),
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) {
@@ -37,40 +40,88 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
void SymbolBucket::upload(gl::Context& context) {
if (hasTextData()) {
- text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices));
- text.indexBuffer = context.createIndexBuffer(std::move(text.triangles));
- textSizeBinder->upload(context);
+ 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.indexBuffer = context.createIndexBuffer(std::move(icon.triangles));
- iconSizeBinder->upload(context);
+ 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;
-}
-
-void SymbolBucket::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer,
- const RenderTile& tile) {
- painter.renderSymbol(parameters, *this, *layer.as<RenderSymbolLayer>(), tile);
+ staticUploaded = true;
+ placementChangesUploaded = true;
+ dynamicUploaded = true;
+ sortUploaded = true;
}
bool SymbolBucket::hasData() const {
- assert(false); // Should be calling SymbolLayout::has{Text,Icon,CollisonBox}Data() instead.
- return false;
+ return hasTextData() || hasIconData() || hasCollisionBoxData();
}
bool SymbolBucket::hasTextData() const {
@@ -85,4 +136,83 @@ bool SymbolBucket::hasCollisionBoxData() const {
return !collisionBox.segments.empty();
}
+bool SymbolBucket::hasCollisionCircleData() const {
+ return !collisionCircle.segments.empty();
+}
+
+void SymbolBucket::updateOpacity() {
+ placementChangesUploaded = false;
+ uploaded = false;
+}
+
+void addPlacedSymbol(gl::IndexVector<gl::Triangles>& triangles, const PlacedSymbol& placedSymbol) {
+ auto endIndex = placedSymbol.vertexStartIndex + placedSymbol.glyphOffsets.size() * 4;
+ for (auto vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
+ triangles.emplace_back(vertexIndex + 0, vertexIndex + 1, vertexIndex + 2);
+ triangles.emplace_back(vertexIndex + 1, vertexIndex + 2, vertexIndex + 3);
+ }
+}
+
+void SymbolBucket::sortFeatures(const float angle) {
+ if (!sortFeaturesByY) {
+ return;
+ }
+
+ if (sortedAngle && *sortedAngle == angle) {
+ return;
+ }
+
+ sortedAngle = angle;
+
+ // The current approach to sorting doesn't sort across segments so don't try.
+ // Sorting within segments separately seemed not to be worth the complexity.
+ if (text.segments.size() > 1 || icon.segments.size() > 1) {
+ return;
+ }
+
+ sortUploaded = false;
+ uploaded = false;
+
+ // If the symbols are allowed to overlap sort them by their vertical screen position.
+ // The index array buffer is rewritten to reference the (unchanged) vertices in the
+ // sorted order.
+
+ // To avoid sorting the actual symbolInstance array we sort an array of indexes.
+ std::vector<size_t> symbolInstanceIndexes;
+ symbolInstanceIndexes.reserve(symbolInstances.size());
+ for (size_t i = 0; i < symbolInstances.size(); i++) {
+ symbolInstanceIndexes.push_back(i);
+ }
+
+ const float sin = std::sin(angle);
+ const float cos = std::cos(angle);
+
+ std::sort(symbolInstanceIndexes.begin(), symbolInstanceIndexes.end(), [sin, cos, this](size_t &aIndex, size_t &bIndex) {
+ const SymbolInstance& a = symbolInstances[aIndex];
+ const SymbolInstance& b = symbolInstances[bIndex];
+ const int32_t aRotated = sin * a.anchor.point.x + cos * a.anchor.point.y;
+ const int32_t bRotated = sin * b.anchor.point.x + cos * b.anchor.point.y;
+ return aRotated != bRotated ?
+ aRotated < bRotated :
+ a.index > b.index;
+ });
+
+ text.triangles.clear();
+ icon.triangles.clear();
+
+ for (auto i : symbolInstanceIndexes) {
+ const SymbolInstance& symbolInstance = symbolInstances[i];
+
+ if (symbolInstance.placedTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]);
+ }
+ if (symbolInstance.placedVerticalTextIndex) {
+ addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedVerticalTextIndex]);
+ }
+ if (symbolInstance.placedIconIndex) {
+ addPlacedSymbol(icon.triangles, icon.placedSymbols[*symbolInstance.placedIconIndex]);
+ }
+ }
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp
index 002b6e28b3..4abea90508 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.hpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp
@@ -4,17 +4,39 @@
#include <mbgl/map/mode.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
-#include <mbgl/gl/segment.hpp>
+#include <mbgl/programs/segment.hpp>
#include <mbgl/programs/symbol_program.hpp>
#include <mbgl/programs/collision_box_program.hpp>
#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>
namespace mbgl {
+class PlacedSymbol {
+public:
+ PlacedSymbol(Point<float> anchorPoint_, uint16_t segment_, float lowerSize_, float upperSize_,
+ std::array<float, 2> lineOffset_, WritingModeType writingModes_, GeometryCoordinates line_, std::vector<float> tileDistances_) :
+ anchorPoint(anchorPoint_), segment(segment_), lowerSize(lowerSize_), upperSize(upperSize_),
+ 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;
+ WritingModeType writingModes;
+ GeometryCoordinates line;
+ std::vector<float> tileDistances;
+ std::vector<float> glyphOffsets;
+ bool hidden;
+ size_t vertexStartIndex;
+};
+
class SymbolBucket : public Bucket {
public:
SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated,
@@ -23,18 +45,33 @@ public:
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons,
- bool iconsNeedLinear);
+ bool iconsNeedLinear,
+ bool sortFeaturesByY,
+ const std::vector<SymbolInstance>&&);
void upload(gl::Context&) override;
- void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override;
bool hasData() const override;
bool hasTextData() const;
bool hasIconData() const;
bool hasCollisionBoxData() const;
+ bool hasCollisionCircleData() const;
+
+ void updateOpacity();
+ void sortFeatures(const float angle);
const style::SymbolLayoutProperties::PossiblyEvaluated layout;
const bool sdfIcons;
const bool iconsNeedLinear;
+ const bool sortFeaturesByY;
+
+ optional<float> sortedAngle;
+
+ bool staticUploaded = false;
+ bool placementChangesUploaded = false;
+ bool dynamicUploaded = false;
+ bool sortUploaded = false;
+
+ std::vector<SymbolInstance> symbolInstances;
std::map<std::string, std::pair<
SymbolIconProgram::PaintPropertyBinders,
@@ -44,10 +81,15 @@ public:
struct TextBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
+ gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<SymbolTextAttributes> segments;
+ 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;
@@ -55,22 +97,39 @@ public:
struct IconBuffer {
gl::VertexVector<SymbolLayoutVertex> vertices;
+ gl::VertexVector<SymbolDynamicLayoutAttributes::Vertex> dynamicVertices;
+ gl::VertexVector<SymbolOpacityAttributes::Vertex> opacityVertices;
gl::IndexVector<gl::Triangles> triangles;
- gl::SegmentVector<SymbolIconAttributes> segments;
+ SegmentVector<SymbolIconAttributes> segments;
+ std::vector<PlacedSymbol> placedSymbols;
PremultipliedImage atlasImage;
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;
- gl::SegmentVector<CollisionBoxAttributes> segments;
+ struct CollisionBuffer {
+ gl::VertexVector<CollisionBoxLayoutAttributes::Vertex> vertices;
+ gl::VertexVector<CollisionBoxDynamicAttributes::Vertex> dynamicVertices;
+ SegmentVector<CollisionBoxProgram::Attributes> segments;
+
+ optional<gl::VertexBuffer<CollisionBoxLayoutAttributes::Vertex>> vertexBuffer;
+ optional<gl::VertexBuffer<CollisionBoxDynamicAttributes::Vertex>> dynamicVertexBuffer;
+ };
- optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer;
+ 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;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/cross_faded_property_evaluator.cpp b/src/mbgl/renderer/cross_faded_property_evaluator.cpp
index ee3c86614f..4dff9dbf12 100644
--- a/src/mbgl/renderer/cross_faded_property_evaluator.cpp
+++ b/src/mbgl/renderer/cross_faded_property_evaluator.cpp
@@ -27,7 +27,10 @@ Faded<T> CrossFadedPropertyEvaluator<T>::calculate(const T& min, const T& mid, c
const float z = parameters.z;
const float fraction = z - std::floor(z);
const std::chrono::duration<float> d = parameters.defaultFadeDuration;
- const float t = std::min((parameters.now - parameters.zoomHistory.lastIntegerZoomTime) / d, 1.0f);
+ const float t =
+ d != std::chrono::duration<float>::zero()
+ ? std::min((parameters.now - parameters.zoomHistory.lastIntegerZoomTime) / d, 1.0f)
+ : 1.0f;
return z > parameters.zoomHistory.lastIntegerZoom
? Faded<T> { min, mid, 2.0f, 1.0f, fraction + (1.0f - fraction) * t }
diff --git a/src/mbgl/renderer/frame_history.cpp b/src/mbgl/renderer/frame_history.cpp
deleted file mode 100644
index 869222b4eb..0000000000
--- a/src/mbgl/renderer/frame_history.cpp
+++ /dev/null
@@ -1,77 +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);
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/frame_history.hpp b/src/mbgl/renderer/frame_history.hpp
deleted file mode 100644
index f2b11f5f41..0000000000
--- a/src/mbgl/renderer/frame_history.hpp
+++ /dev/null
@@ -1,40 +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);
-
-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/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp
index d0a106ede6..2ef6be0c4f 100644
--- a/src/mbgl/renderer/image_manager.cpp
+++ b/src/mbgl/renderer/image_manager.cpp
@@ -4,12 +4,23 @@
namespace mbgl {
-void ImageManager::onSpriteLoaded() {
- loaded = true;
- for (const auto& entry : requestors) {
- notify(*entry.first, entry.second);
+void ImageManager::setLoaded(bool loaded_) {
+ if (loaded == loaded_) {
+ return;
+ }
+
+ loaded = loaded_;
+
+ if (loaded) {
+ for (const auto& entry : requestors) {
+ notify(*entry.first, entry.second);
+ }
+ requestors.clear();
}
- requestors.clear();
+}
+
+bool ImageManager::isLoaded() const {
+ return loaded;
}
void ImageManager::addImage(Immutable<style::Image::Impl> image_) {
@@ -28,6 +39,13 @@ void ImageManager::removeImage(const std::string& id) {
auto it = patterns.find(id);
if (it != patterns.end()) {
+ // Clear pattern from the atlas image.
+ const uint32_t x = it->second.bin->x;
+ const uint32_t y = it->second.bin->y;
+ const uint32_t w = it->second.bin->w;
+ const uint32_t h = it->second.bin->h;
+ PremultipliedImage::clear(atlasImage, { x, y }, { w, h });
+
shelfPack.unref(*it->second.bin);
patterns.erase(it);
}
@@ -41,23 +59,23 @@ const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
return nullptr;
}
-void ImageManager::getImages(ImageRequestor& requestor, ImageDependencies dependencies) {
+void ImageManager::getImages(ImageRequestor& requestor, ImageRequestPair&& pair) {
// If the sprite has been loaded, or if all the icon dependencies are already present
// (i.e. if they've been addeded via runtime styling), then notify the requestor immediately.
// Otherwise, delay notification until the sprite is loaded. At that point, if any of the
// dependencies are still unavailable, we'll just assume they are permanently missing.
bool hasAllDependencies = true;
if (!isLoaded()) {
- for (const auto& dependency : dependencies) {
+ for (const auto& dependency : pair.first) {
if (images.find(dependency) == images.end()) {
hasAllDependencies = false;
}
}
}
if (isLoaded() || hasAllDependencies) {
- notify(requestor, dependencies);
+ notify(requestor, std::move(pair));
} else {
- requestors.emplace(&requestor, std::move(dependencies));
+ requestors.emplace(&requestor, std::move(pair));
}
}
@@ -65,17 +83,17 @@ void ImageManager::removeRequestor(ImageRequestor& requestor) {
requestors.erase(&requestor);
}
-void ImageManager::notify(ImageRequestor& requestor, const ImageDependencies& dependencies) const {
+void ImageManager::notify(ImageRequestor& requestor, const ImageRequestPair& pair) const {
ImageMap response;
- for (const auto& dependency : dependencies) {
+ for (const auto& dependency : pair.first) {
auto it = images.find(dependency);
if (it != images.end()) {
response.emplace(*it);
}
}
- requestor.onImagesAvailable(response);
+ requestor.onImagesAvailable(response, pair.second);
}
void ImageManager::dumpDebugLogs() const {
diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp
index 9a9a4ce997..f72ba9fb53 100644
--- a/src/mbgl/renderer/image_manager.hpp
+++ b/src/mbgl/renderer/image_manager.hpp
@@ -21,7 +21,7 @@ class Context;
class ImageRequestor {
public:
virtual ~ImageRequestor() = default;
- virtual void onImagesAvailable(ImageMap) = 0;
+ virtual void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) = 0;
};
/*
@@ -39,11 +39,8 @@ public:
ImageManager();
~ImageManager();
- void onSpriteLoaded();
-
- bool isLoaded() const {
- return loaded;
- }
+ void setLoaded(bool);
+ bool isLoaded() const;
void dumpDebugLogs() const;
@@ -53,15 +50,15 @@ public:
void updateImage(Immutable<style::Image::Impl>);
void removeImage(const std::string&);
- void getImages(ImageRequestor&, ImageDependencies);
+ void getImages(ImageRequestor&, ImageRequestPair&&);
void removeRequestor(ImageRequestor&);
private:
- void notify(ImageRequestor&, const ImageDependencies&) const;
+ void notify(ImageRequestor&, const ImageRequestPair&) const;
bool loaded = false;
- std::unordered_map<ImageRequestor*, ImageDependencies> requestors;
+ std::unordered_map<ImageRequestor*, ImageRequestPair> requestors;
ImageMap images;
// Pattern stuff
diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp
index 702977cd23..9fddba3f74 100644
--- a/src/mbgl/renderer/layers/render_background_layer.cpp
+++ b/src/mbgl/renderer/layers/render_background_layer.cpp
@@ -1,9 +1,17 @@
#include <mbgl/renderer/layers/render_background_layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#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/util/tile_cover.hpp>
namespace mbgl {
+using namespace style;
+
RenderBackgroundLayer::RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl> _impl)
: RenderLayer(style::LayerType::Background, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -34,4 +42,74 @@ bool RenderBackgroundLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+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);
+
+ if (!evaluated.get<BackgroundPattern>().to.empty()) {
+ optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from);
+ optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().to);
+
+ if (!imagePosA || !imagePosB)
+ return;
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
+ parameters.programs.fillPattern.get(properties).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ FillPatternUniforms::values(
+ parameters.matrixForTile(tileID),
+ parameters.context.viewport.getCurrentValue().size,
+ parameters.imageManager.getPixelSize(),
+ *imagePosA,
+ *imagePosB,
+ evaluated.get<BackgroundPattern>(),
+ tileID,
+ parameters.state
+ ),
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.tileTriangleSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ } else {
+ for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
+ parameters.programs.fill.get(properties).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ FillProgram::UniformValues {
+ uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) },
+ uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
+ },
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.tileTriangleSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ }
+}
+
} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_background_layer.hpp b/src/mbgl/renderer/layers/render_background_layer.hpp
index 0fba3d2bb1..a619670ee4 100644
--- a/src/mbgl/renderer/layers/render_background_layer.hpp
+++ b/src/mbgl/renderer/layers/render_background_layer.hpp
@@ -14,6 +14,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp
index 51a5ecd7d6..fe2e7cd42d 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.cpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.cpp
@@ -1,5 +1,10 @@
#include <mbgl/renderer/layers/render_circle_layer.hpp>
#include <mbgl/renderer/buckets/circle_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/circle_program.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/circle_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
@@ -7,6 +12,8 @@
namespace mbgl {
+using namespace style;
+
RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl)
: RenderLayer(style::LayerType::Circle, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -40,6 +47,52 @@ bool RenderCircleLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ const bool scaleWithMap = evaluated.get<CirclePitchScale>() == CirclePitchScaleType::Map;
+ const bool pitchWithMap = evaluated.get<CirclePitchAlignment>() == AlignmentType::Map;
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl)));
+ CircleBucket& bucket = *reinterpret_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.circle.get(evaluated).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ parameters.mapMode != MapMode::Continuous
+ ? parameters.stencilModeForClipping(tile.clip)
+ : gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ CircleProgram::UniformValues {
+ uniforms::u_matrix::Value{
+ tile.translatedMatrix(evaluated.get<CircleTranslate>(),
+ evaluated.get<CircleTranslateAnchor>(),
+ parameters.state)
+ },
+ uniforms::u_scale_with_map::Value{ scaleWithMap },
+ uniforms::u_extrude_scale::Value{ pitchWithMap
+ ? std::array<float, 2> {{
+ tile.id.pixelsToTileUnits(1, parameters.state.getZoom()),
+ tile.id.pixelsToTileUnits(1, parameters.state.getZoom()) }}
+ : parameters.pixelsToGLUnits },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() },
+ uniforms::u_pitch_with_map::Value{ pitchWithMap }
+ },
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+}
+
bool RenderCircleLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp
index 4ae7399ad1..f31715f98f 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.hpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.hpp
@@ -14,6 +14,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp
index 4d6084075d..7ece3970da 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.cpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.cpp
@@ -1,9 +1,10 @@
#include <mbgl/renderer/layers/render_custom_layer.hpp>
-#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/bucket.hpp>
#include <mbgl/style/layers/custom_layer_impl.hpp>
#include <mbgl/map/transform_state.hpp>
-#include <mbgl/map/backend_scope.hpp>
namespace mbgl {
@@ -15,8 +16,12 @@ RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
RenderCustomLayer::~RenderCustomLayer() {
assert(BackendScope::exists());
- if (initialized && impl().deinitializeFn) {
- impl().deinitializeFn(impl().context);
+ if (initialized) {
+ if (contextDestroyed && impl().contextLostFn ) {
+ impl().contextLostFn(impl().context);
+ } else if (!contextDestroyed && impl().deinitializeFn) {
+ impl().deinitializeFn(impl().context);
+ }
}
}
@@ -37,21 +42,21 @@ std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&,
return nullptr;
}
-void RenderCustomLayer::render(Painter& painter, PaintParameters& paintParameters, RenderSource*) {
+void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) {
if (!initialized) {
assert(impl().initializeFn);
impl().initializeFn(impl().context);
initialized = true;
}
- gl::Context& context = painter.context;
- const TransformState& state = painter.state;
+ gl::Context& context = paintParameters.context;
+ const TransformState& state = paintParameters.state;
// Reset GL state to a known state so the CustomLayer always has a clean slate.
- context.vertexArrayObject = 0;
- context.setDepthMode(painter.depthModeForSublayer(0, gl::DepthMode::ReadOnly));
+ context.bindVertexArray = 0;
+ context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly));
context.setStencilMode(gl::StencilMode::disabled());
- context.setColorMode(painter.colorModeForRenderPass());
+ context.setColorMode(paintParameters.colorModeForRenderPass());
CustomLayerRenderParameters parameters;
@@ -69,7 +74,7 @@ void RenderCustomLayer::render(Painter& painter, PaintParameters& paintParameter
// Reset the view back to our original one, just in case the CustomLayer changed
// the viewport or Framebuffer.
- paintParameters.view.bind();
+ paintParameters.backend.bind();
context.setDirtyState();
}
diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp
index dd52d315cf..32ed9da8da 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.hpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.hpp
@@ -15,12 +15,17 @@ public:
bool hasTransition() const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const final;
- void render(Painter&, PaintParameters&, RenderSource*) final;
+ void render(PaintParameters&, RenderSource*) final;
const style::CustomLayer::Impl& impl() const;
+ void markContextDestroyed() {
+ contextDestroyed = true;
+ };
+
private:
bool initialized = false;
+ bool contextDestroyed = false;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
index cd69316670..fbd6160e8a 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -1,5 +1,13 @@
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_extrusion_program.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
@@ -7,6 +15,8 @@
namespace mbgl {
+using namespace style;
+
RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl)
: RenderLayer(style::LayerType::FillExtrusion, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -27,14 +37,116 @@ void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters
void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) {
evaluated = unevaluated.evaluate(parameters);
- passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent
- : RenderPass::None;
+ passes = (evaluated.get<style::FillExtrusionOpacity>() > 0)
+ ? (RenderPass::Translucent | RenderPass::Pass3D)
+ : RenderPass::None;
}
bool RenderFillExtrusionLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ if (parameters.pass == RenderPass::Pass3D) {
+ const auto& size = parameters.staticData.backendSize;
+
+ if (!renderTexture || renderTexture->getSize() != size) {
+ renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer);
+ }
+
+ renderTexture->bind();
+
+ optional<float> depthClearValue = {};
+ if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0;
+ // Flag the depth buffer as no longer needing to be cleared for the remainder of this pass.
+ parameters.staticData.depthRenderbuffer->shouldClear(false);
+
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {});
+
+ if (evaluated.get<FillExtrusionPattern>().from.empty()) {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillExtrusionBucket& bucket =
+ *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.fillExtrusion.get(evaluated).draw(
+ parameters.context, gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ FillExtrusionUniforms::values(
+ tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
+ evaluated.get<FillExtrusionTranslateAnchor>(),
+ parameters.state),
+ parameters.state, parameters.evaluatedLight),
+ *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
+ getID());
+ }
+ } else {
+ optional<ImagePosition> imagePosA =
+ parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from);
+ optional<ImagePosition> imagePosB =
+ parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to);
+
+ if (!imagePosA || !imagePosB) {
+ return;
+ }
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillExtrusionBucket& bucket =
+ *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.fillExtrusionPattern.get(evaluated).draw(
+ parameters.context, gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ FillExtrusionPatternUniforms::values(
+ tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
+ evaluated.get<FillExtrusionTranslateAnchor>(),
+ parameters.state),
+ parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB,
+ evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state,
+ -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
+ parameters.evaluatedLight),
+ *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
+ getID());
+ }
+ }
+
+ } else if (parameters.pass == RenderPass::Translucent) {
+ parameters.context.bindTexture(renderTexture->getTexture());
+
+ 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.extrusionTexture.draw(
+ parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ ExtrusionTextureProgram::UniformValues{
+ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
+ uniforms::u_image::Value{ 0 },
+ uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } },
+ parameters.staticData.extrusionTextureVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.extrusionTextureSegments,
+ ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
+ parameters.state.getZoom(), getID());
+ }
+}
+
bool RenderFillExtrusionLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
index 1a55b56836..838494cf91 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
@@ -3,6 +3,8 @@
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/offscreen_texture.hpp>
namespace mbgl {
@@ -14,6 +16,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
@@ -29,6 +32,8 @@ public:
style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated;
const style::FillExtrusionLayer::Impl& impl() const;
+
+ optional<OffscreenTexture> renderTexture;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp
index f1c7e97067..22cb9563c1 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -1,5 +1,11 @@
#include <mbgl/renderer/layers/render_fill_layer.hpp>
#include <mbgl/renderer/buckets/fill_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_program.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
@@ -7,6 +13,8 @@
namespace mbgl {
+using namespace style;
+
RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl)
: RenderLayer(style::LayerType::Fill, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -50,6 +58,132 @@ bool RenderFillLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (evaluated.get<FillPattern>().from.empty()) {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program,
+ const auto& drawMode,
+ const auto& depthMode,
+ const auto& indexBuffer,
+ const auto& segments) {
+ program.get(evaluated).draw(
+ parameters.context,
+ drawMode,
+ depthMode,
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ FillProgram::UniformValues {
+ uniforms::u_matrix::Value{
+ tile.translatedMatrix(evaluated.get<FillTranslate>(),
+ evaluated.get<FillTranslateAnchor>(),
+ parameters.state)
+ },
+ uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
+ },
+ *bucket.vertexBuffer,
+ indexBuffer,
+ segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ // Only draw the fill when it's opaque and we're drawing opaque fragments,
+ // or when it's translucent and we're drawing translucent fragments.
+ if ((evaluated.get<FillColor>().constantOr(Color()).a >= 1.0f
+ && evaluated.get<FillOpacity>().constantOr(0) >= 1.0f) == (parameters.pass == RenderPass::Opaque)) {
+ draw(parameters.programs.fill,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque
+ ? gl::DepthMode::ReadWrite
+ : gl::DepthMode::ReadOnly),
+ *bucket.triangleIndexBuffer,
+ bucket.triangleSegments);
+ }
+
+ if (evaluated.get<FillAntialias>() && parameters.pass == RenderPass::Translucent) {
+ draw(parameters.programs.fillOutline,
+ gl::Lines{ 2.0f },
+ parameters.depthModeForSublayer(
+ unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0,
+ gl::DepthMode::ReadOnly),
+ *bucket.lineIndexBuffer,
+ bucket.lineSegments);
+ }
+ }
+ } else {
+ if (parameters.pass != RenderPass::Translucent) {
+ return;
+ }
+
+ optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillPattern>().from);
+ optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillPattern>().to);
+
+ if (!imagePosA || !imagePosB) {
+ return;
+ }
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program,
+ const auto& drawMode,
+ const auto& depthMode,
+ const auto& indexBuffer,
+ const auto& segments) {
+ program.get(evaluated).draw(
+ parameters.context,
+ drawMode,
+ depthMode,
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ FillPatternUniforms::values(
+ tile.translatedMatrix(evaluated.get<FillTranslate>(),
+ evaluated.get<FillTranslateAnchor>(),
+ parameters.state),
+ parameters.context.viewport.getCurrentValue().size,
+ parameters.imageManager.getPixelSize(),
+ *imagePosA,
+ *imagePosB,
+ evaluated.get<FillPattern>(),
+ tile.id,
+ parameters.state
+ ),
+ *bucket.vertexBuffer,
+ indexBuffer,
+ segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ draw(parameters.programs.fillPattern,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(1, gl::DepthMode::ReadWrite),
+ *bucket.triangleIndexBuffer,
+ bucket.triangleSegments);
+
+ if (evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined()) {
+ draw(parameters.programs.fillOutlinePattern,
+ gl::Lines { 2.0f },
+ parameters.depthModeForSublayer(2, gl::DepthMode::ReadOnly),
+ *bucket.lineIndexBuffer,
+ bucket.lineSegments);
+ }
+ }
+ }
+}
+
bool RenderFillLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp
index 1960fb653f..a51865698f 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.hpp
@@ -14,6 +14,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
index c27e5ea990..1b4a1c0ff7 100644
--- a/src/mbgl/renderer/layers/render_line_layer.cpp
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -1,5 +1,12 @@
#include <mbgl/renderer/layers/render_line_layer.hpp>
#include <mbgl/renderer/buckets/line_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/line_program.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/line_layer_impl.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
@@ -7,6 +14,8 @@
namespace mbgl {
+using namespace style;
+
RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl)
: RenderLayer(style::LayerType::Line, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -43,6 +52,82 @@ bool RenderLineLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<LineBucket*>(tile.tile.getBucket(*baseImpl)));
+ LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program, auto&& uniformValues) {
+ program.get(evaluated).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ std::move(uniformValues),
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ if (!evaluated.get<LineDasharray>().from.empty()) {
+ const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round
+ ? LinePatternCap::Round : LinePatternCap::Square;
+ LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().from, cap);
+ LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().to, cap);
+
+ parameters.lineAtlas.bind(parameters.context, 0);
+
+ draw(parameters.programs.lineSDF,
+ LineSDFProgram::uniformValues(
+ evaluated,
+ parameters.pixelRatio,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits,
+ posA,
+ posB,
+ parameters.lineAtlas.getSize().width));
+
+ } else if (!evaluated.get<LinePattern>().from.empty()) {
+ optional<ImagePosition> posA = parameters.imageManager.getPattern(evaluated.get<LinePattern>().from);
+ optional<ImagePosition> posB = parameters.imageManager.getPattern(evaluated.get<LinePattern>().to);
+
+ if (!posA || !posB)
+ return;
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ draw(parameters.programs.linePattern,
+ LinePatternProgram::uniformValues(
+ evaluated,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits,
+ parameters.imageManager.getPixelSize(),
+ *posA,
+ *posB));
+
+ } else {
+ draw(parameters.programs.line,
+ LineProgram::uniformValues(
+ evaluated,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits));
+ }
+ }
+}
+
optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) {
if (offset == 0) return {};
diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp
index 77551b6b7c..8bf7e2329d 100644
--- a/src/mbgl/renderer/layers/render_line_layer.hpp
+++ b/src/mbgl/renderer/layers/render_line_layer.hpp
@@ -23,6 +23,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
bool queryIntersectsFeature(
const GeometryCoordinates&,
diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp
index 84e27a3895..06616d90e5 100644
--- a/src/mbgl/renderer/layers/render_raster_layer.cpp
+++ b/src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -1,14 +1,18 @@
#include <mbgl/renderer/layers/render_raster_layer.hpp>
-#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/style/layers/raster_layer_impl.hpp>
-#include <mbgl/gl/context.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/tile/tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/raster_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/raster_layer_impl.hpp>
namespace mbgl {
+using namespace style;
+
RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl)
: RenderLayer(style::LayerType::Raster, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -37,12 +41,113 @@ bool RenderRasterLayer::hasTransition() const {
return unevaluated.hasTransition();
}
-void RenderRasterLayer::render(Painter& painter, PaintParameters& parameters, RenderSource* source) {
- RenderLayer::render(painter, parameters, source);
- if (renderTiles.empty()) {
- RenderImageSource* imageSource = source->as<RenderImageSource>();
- if (imageSource) {
- imageSource->render(painter, parameters, *this);
+static float saturationFactor(float saturation) {
+ if (saturation > 0) {
+ return 1 - 1 / (1.001 - saturation);
+ } else {
+ return -saturation;
+ }
+}
+
+static float contrastFactor(float contrast) {
+ if (contrast > 0) {
+ return 1 / (1 - contrast);
+ } else {
+ return 1 + contrast;
+ }
+}
+
+static std::array<float, 3> spinWeights(float spin) {
+ spin *= util::DEG2RAD;
+ float s = std::sin(spin);
+ float c = std::cos(spin);
+ std::array<float, 3> spin_weights = {{
+ (2 * c + 1) / 3,
+ (-std::sqrt(3.0f) * s - c + 1) / 3,
+ (std::sqrt(3.0f) * s - c + 1) / 3
+ }};
+ return spin_weights;
+}
+
+void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source) {
+ if (parameters.pass != RenderPass::Translucent)
+ return;
+
+ auto draw = [&] (const mat4& matrix,
+ const auto& vertexBuffer,
+ const auto& indexBuffer,
+ const auto& segments) {
+ parameters.programs.raster.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ RasterProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_image0::Value{ 0 },
+ uniforms::u_image1::Value{ 1 },
+ uniforms::u_opacity::Value{ evaluated.get<RasterOpacity>() },
+ uniforms::u_fade_t::Value{ 1 },
+ uniforms::u_brightness_low::Value{ evaluated.get<RasterBrightnessMin>() },
+ uniforms::u_brightness_high::Value{ evaluated.get<RasterBrightnessMax>() },
+ uniforms::u_saturation_factor::Value{ saturationFactor(evaluated.get<RasterSaturation>()) },
+ uniforms::u_contrast_factor::Value{ contrastFactor(evaluated.get<RasterContrast>()) },
+ uniforms::u_spin_weights::Value{ spinWeights(evaluated.get<RasterHueRotate>()) },
+ uniforms::u_buffer_scale::Value{ 1.0f },
+ uniforms::u_scale_parent::Value{ 1.0f },
+ uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} },
+ },
+ vertexBuffer,
+ indexBuffer,
+ segments,
+ RasterProgram::PaintPropertyBinders { evaluated, 0 },
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ if (RenderImageSource* imageSource = source->as<RenderImageSource>()) {
+ if (imageSource->isEnabled() && imageSource->isLoaded() && !imageSource->bucket->needsUpload()) {
+ RasterBucket& bucket = *imageSource->bucket;
+
+ assert(bucket.texture);
+ parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear);
+ parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear);
+
+ for (auto matrix_ : imageSource->matrices) {
+ draw(matrix_,
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments);
+ }
+ }
+ } else {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl)));
+ RasterBucket& bucket = *reinterpret_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl));
+
+ if (!bucket.hasData())
+ continue;
+
+ assert(bucket.texture);
+ parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear);
+ parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear);
+
+ 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,
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments);
+ } else {
+ // Draw the full tile.
+ draw(tile.matrix,
+ parameters.staticData.rasterVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.rasterSegments);
+ }
}
}
}
diff --git a/src/mbgl/renderer/layers/render_raster_layer.hpp b/src/mbgl/renderer/layers/render_raster_layer.hpp
index ce46152a95..87de316f7c 100644
--- a/src/mbgl/renderer/layers/render_raster_layer.hpp
+++ b/src/mbgl/renderer/layers/render_raster_layer.hpp
@@ -15,7 +15,7 @@ public:
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
- void render(Painter&, PaintParameters&, RenderSource*) override;
+ void render(PaintParameters&, RenderSource*) override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp
index 6540fc9612..04fcb2c3ab 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.cpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp
@@ -1,13 +1,27 @@
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/layout/symbol_layout.hpp>
-#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/renderer/property_evaluation_parameters.hpp>
-#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/symbol_program.hpp>
+#include <mbgl/programs/collision_box_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/tile/geometry_tile.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/layout/symbol_layout.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+#include <mbgl/util/math.hpp>
+
+#include <cmath>
namespace mbgl {
+using namespace style;
+
RenderSymbolLayer::RenderSymbolLayer(Immutable<style::SymbolLayer::Impl> _impl)
: RenderLayer(style::LayerType::Symbol, _impl),
unevaluated(impl().paint.untransitioned()) {
@@ -55,6 +69,224 @@ bool RenderSymbolLayer::hasTransition() const {
return unevaluated.hasTransition();
}
+void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl)));
+ SymbolBucket& bucket = *reinterpret_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl));
+
+ const auto& layout = bucket.layout;
+
+ auto draw = [&] (auto& program,
+ auto&& uniformValues,
+ const auto& buffers,
+ const auto& symbolSizeBinder,
+ const SymbolPropertyValues& values_,
+ const auto& binders,
+ const auto& paintProperties)
+ {
+ program.get(paintProperties).draw(
+ parameters.context,
+ gl::Triangles(),
+ values_.pitchAlignment == AlignmentType::Map
+ ? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)
+ : gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ std::move(uniformValues),
+ *buffers.vertexBuffer,
+ *buffers.dynamicVertexBuffer,
+ *buffers.opacityVertexBuffer,
+ *symbolSizeBinder,
+ *buffers.indexBuffer,
+ buffers.segments,
+ binders,
+ paintProperties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ assert(dynamic_cast<GeometryTile*>(&tile.tile));
+ GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile);
+
+ if (bucket.hasIconData()) {
+ auto values = iconPropertyValues(layout);
+ auto paintPropertyValues = iconPaintProperties();
+
+ const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line &&
+ layout.get<IconRotationAlignment>() == AlignmentType::Map;
+
+ if (alongLine) {
+ reprojectLineLabels(bucket.icon.dynamicVertices,
+ bucket.icon.placedSymbols,
+ tile.matrix,
+ values,
+ tile,
+ *bucket.iconSizeBinder,
+ parameters.state);
+
+ parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices));
+ }
+
+ const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear;
+ const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0;
+
+ parameters.context.bindTexture(*geometryTile.iconAtlasTexture, 0,
+ bucket.sdfIcons || parameters.state.isChanging() || iconScaled || iconTransformed
+ ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest);
+
+ const Size texsize = geometryTile.iconAtlasTexture->size;
+
+ if (bucket.sdfIcons) {
+ if (values.hasHalo) {
+ draw(parameters.programs.symbolIconSDF,
+ SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+
+ if (values.hasFill) {
+ draw(parameters.programs.symbolIconSDF,
+ SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+ } else {
+ draw(parameters.programs.symbolIcon,
+ SymbolIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+ }
+
+ if (bucket.hasTextData()) {
+ parameters.context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear);
+
+ auto values = textPropertyValues(layout);
+ auto paintPropertyValues = textPaintProperties();
+
+ const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line &&
+ layout.get<TextRotationAlignment>() == AlignmentType::Map;
+
+ if (alongLine) {
+ reprojectLineLabels(bucket.text.dynamicVertices,
+ bucket.text.placedSymbols,
+ tile.matrix,
+ values,
+ tile,
+ *bucket.textSizeBinder,
+ parameters.state);
+
+ parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices));
+ }
+
+ const Size texsize = geometryTile.glyphAtlasTexture->size;
+
+ if (values.hasHalo) {
+ draw(parameters.programs.symbolGlyph,
+ SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
+ bucket.text,
+ bucket.textSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).second,
+ paintPropertyValues);
+ }
+
+ if (values.hasFill) {
+ draw(parameters.programs.symbolGlyph,
+ SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
+ bucket.text,
+ bucket.textSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).second,
+ paintPropertyValues);
+ }
+ }
+
+ if (bucket.hasCollisionBoxData()) {
+ 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(),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ CollisionBoxProgram::UniformValues {
+ uniforms::u_matrix::Value{ tile.matrix },
+ 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,
+ properties,
+ parameters.state.getZoom(),
+ 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(),
+ CollisionBoxProgram::UniformValues {
+ uniforms::u_matrix::Value{ tile.matrix },
+ uniforms::u_extrude_scale::Value{ extrudeScale },
+ 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()
+ );
+
+ }
+ }
+}
+
style::IconPaintProperties::PossiblyEvaluated RenderSymbolLayer::iconPaintProperties() const {
return style::IconPaintProperties::PossiblyEvaluated {
evaluated.get<style::IconOpacity>(),
@@ -82,25 +314,41 @@ style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProper
style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const {
return style::SymbolPropertyValues {
- layout_.get<style::IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment
+ layout_.get<style::IconPitchAlignment>(),
layout_.get<style::IconRotationAlignment>(),
+ layout_.get<style::IconKeepUpright>(),
evaluated.get<style::IconTranslate>(),
evaluated.get<style::IconTranslateAnchor>(),
evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 &&
evaluated.get<style::IconHaloWidth>().constantOr(1),
- evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0
+ evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0,
+ 10.0f
};
}
style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const {
+ // We hide line labels with viewport alignment as they move into the distance
+ // because the approximations we use for drawing their glyphs get progressively worse
+ // The "1.5" here means we start hiding them when the distance from the label
+ // to the camera is 50% greater than the distance from the center of the map
+ // to the camera. Depending on viewport properties, you might expect this to filter
+ // the top third of the screen at pitch 60, and do almost nothing at pitch 45
+ // "10" is effectively infinite at any pitch we support
+ const bool limitMaxDistance =
+ layout_.get<style::SymbolPlacement>() == style::SymbolPlacementType::Line
+ && layout_.get<style::TextRotationAlignment>() == style::AlignmentType::Map
+ && layout_.get<style::TextPitchAlignment>() == style::AlignmentType::Viewport;
+
return style::SymbolPropertyValues {
layout_.get<style::TextPitchAlignment>(),
layout_.get<style::TextRotationAlignment>(),
+ layout_.get<style::TextKeepUpright>(),
evaluated.get<style::TextTranslate>(),
evaluated.get<style::TextTranslateAnchor>(),
evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 &&
evaluated.get<style::TextHaloWidth>().constantOr(1),
- evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0
+ evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0,
+ limitMaxDistance ? 1.5f : 10.0f
};
}
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp
index 2199103de2..83709b5122 100644
--- a/src/mbgl/renderer/layers/render_symbol_layer.hpp
+++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp
@@ -40,6 +40,7 @@ public:
// Layout
AlignmentType pitchAlignment;
AlignmentType rotationAlignment;
+ bool keepUpright;
// Paint
std::array<float, 2> translate;
@@ -47,6 +48,8 @@ public:
bool hasHalo;
bool hasFill;
+
+ float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else
};
} // namespace style
@@ -63,6 +66,7 @@ public:
void transition(const TransitionParameters&) override;
void evaluate(const PropertyEvaluationParameters&) override;
bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
style::IconPaintProperties::PossiblyEvaluated iconPaintProperties() const;
style::TextPaintProperties::PossiblyEvaluated textPaintProperties() const;
diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp
new file mode 100644
index 0000000000..58dd5597a5
--- /dev/null
+++ b/src/mbgl/renderer/paint_parameters.cpp
@@ -0,0 +1,96 @@
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+namespace mbgl {
+
+PaintParameters::PaintParameters(gl::Context& context_,
+ float pixelRatio_,
+ GLContextMode contextMode_,
+ RendererBackend& backend_,
+ const UpdateParameters& updateParameters,
+ const EvaluatedLight& evaluatedLight_,
+ RenderStaticData& staticData_,
+ ImageManager& imageManager_,
+ LineAtlas& lineAtlas_)
+ : context(context_),
+ backend(backend_),
+ state(updateParameters.transformState),
+ evaluatedLight(evaluatedLight_),
+ staticData(staticData_),
+ imageManager(imageManager_),
+ lineAtlas(lineAtlas_),
+ mapMode(updateParameters.mode),
+ debugOptions(updateParameters.debugOptions),
+ contextMode(contextMode_),
+ timePoint(updateParameters.timePoint),
+ pixelRatio(pixelRatio_),
+#ifndef NDEBUG
+ programs((debugOptions & MapDebugOptions::Overdraw) ? staticData_.overdrawPrograms : staticData_.programs)
+#else
+ programs(staticData_.programs)
+#endif
+{
+ // Update the default matrices to the current viewport dimensions.
+ state.getProjMatrix(projMatrix);
+
+ // 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.
+ state.getProjMatrix(nearClippedProjMatrix, 100);
+
+ pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }};
+
+ if (state.getViewportMode() == ViewportMode::FlippedY) {
+ pixelsToGLUnits[1] *= -1;
+ }
+}
+
+mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID) {
+ mat4 matrix;
+ state.matrixFor(matrix, tileID);
+ matrix::multiply(matrix, projMatrix, matrix);
+ return matrix;
+}
+
+gl::DepthMode PaintParameters::depthModeForSublayer(uint8_t n, gl::DepthMode::Mask mask) const {
+ float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon;
+ float farDepth = nearDepth + depthRangeSize;
+ return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } };
+}
+
+gl::DepthMode PaintParameters::depthModeFor3D(gl::DepthMode::Mask mask) const {
+ return gl::DepthMode { gl::DepthMode::LessEqual, mask, { 0.0, 1.0 } };
+}
+
+gl::StencilMode PaintParameters::stencilModeForClipping(const ClipID& id) const {
+ return gl::StencilMode {
+ gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) },
+ static_cast<int32_t>(id.reference.to_ulong()),
+ 0,
+ gl::StencilMode::Keep,
+ gl::StencilMode::Keep,
+ gl::StencilMode::Replace
+ };
+}
+
+gl::ColorMode PaintParameters::colorModeForRenderPass() const {
+ if (debugOptions & MapDebugOptions::Overdraw) {
+ const float overdraw = 1.0f / 8.0f;
+ return gl::ColorMode {
+ gl::ColorMode::Add {
+ gl::ColorMode::ConstantColor,
+ gl::ColorMode::One
+ },
+ Color { overdraw, overdraw, overdraw, 0.0f },
+ gl::ColorMode::Mask { true, true, true, true }
+ };
+ } else if (pass == RenderPass::Translucent) {
+ return gl::ColorMode::alphaBlended();
+ } else {
+ return gl::ColorMode::unblended();
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp
index 213c01cfbd..5c934c2239 100644
--- a/src/mbgl/renderer/paint_parameters.hpp
+++ b/src/mbgl/renderer/paint_parameters.hpp
@@ -1,14 +1,78 @@
#pragma once
+#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>
+#include <mbgl/gl/color_mode.hpp>
+#include <mbgl/util/mat4.hpp>
+#include <mbgl/algorithm/generate_clip_ids.hpp>
+
+#include <array>
+
namespace mbgl {
+class RendererBackend;
+class UpdateParameters;
+class RenderStaticData;
class Programs;
-class View;
+class TransformState;
+class ImageManager;
+class LineAtlas;
+class UnwrappedTileID;
class PaintParameters {
public:
+ PaintParameters(gl::Context&,
+ float pixelRatio,
+ GLContextMode,
+ RendererBackend&,
+ const UpdateParameters&,
+ const EvaluatedLight&,
+ RenderStaticData&,
+ ImageManager&,
+ LineAtlas&);
+
+ gl::Context& context;
+ RendererBackend& backend;
+
+ const TransformState& state;
+ const EvaluatedLight& evaluatedLight;
+
+ RenderStaticData& staticData;
+ ImageManager& imageManager;
+ LineAtlas& lineAtlas;
+
+ RenderPass pass = RenderPass::Opaque;
+ MapMode mapMode;
+ MapDebugOptions debugOptions;
+ GLContextMode contextMode;
+ TimePoint timePoint;
+
+ float pixelRatio;
+ std::array<float, 2> pixelsToGLUnits;
+ algorithm::ClipIDGenerator clipIDGenerator;
+
Programs& programs;
- View& view;
+
+ gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const;
+ gl::DepthMode depthModeFor3D(gl::DepthMode::Mask) const;
+ gl::StencilMode stencilModeForClipping(const ClipID&) const;
+ gl::ColorMode colorModeForRenderPass() const;
+
+ mat4 matrixForTile(const UnwrappedTileID&);
+
+ mat4 projMatrix;
+ 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 f78147fc87..3a49882f12 100644
--- a/src/mbgl/renderer/paint_property_binder.hpp
+++ b/src/mbgl/renderer/paint_property_binder.hpp
@@ -3,6 +3,7 @@
#include <mbgl/programs/attributes.hpp>
#include <mbgl/gl/attribute.hpp>
#include <mbgl/gl/uniform.hpp>
+#include <mbgl/gl/context.hpp>
#include <mbgl/util/type_list.hpp>
#include <mbgl/renderer/possibly_evaluated_property_value.hpp>
#include <mbgl/renderer/paint_property_statistics.hpp>
@@ -81,7 +82,7 @@ public:
virtual void populateVertexVector(const GeometryTileFeature& feature, std::size_t length) = 0;
virtual void upload(gl::Context& context) = 0;
- virtual AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0;
+ virtual optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0;
virtual float interpolationFactor(float currentZoom) const = 0;
virtual T uniformValue(const PossiblyEvaluatedPropertyValue<T>& currentValue) const = 0;
@@ -103,8 +104,8 @@ public:
void populateVertexVector(const GeometryTileFeature&, std::size_t) override {}
void upload(gl::Context&) override {}
- AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override {
- return gl::DisabledAttribute();
+ optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>&) const override {
+ return {};
}
float interpolationFactor(float) const override {
@@ -147,9 +148,9 @@ public:
vertexBuffer = context.createVertexBuffer(std::move(vertexVector));
}
- AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
+ optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
if (currentValue.isConstant()) {
- return gl::DisabledAttribute();
+ return {};
} else {
return Attribute::binding(*vertexBuffer, 0, BaseAttribute::Dimensions);
}
@@ -189,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(
@@ -208,9 +209,9 @@ public:
vertexBuffer = context.createVertexBuffer(std::move(vertexVector));
}
- AttributeBinding attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
+ optional<AttributeBinding> attributeBinding(const PossiblyEvaluatedPropertyValue<T>& currentValue) const override {
if (currentValue.isConstant()) {
- return gl::DisabledAttribute();
+ return {};
} else {
return Attribute::binding(*vertexBuffer, 0);
}
@@ -218,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);
}
}
@@ -236,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/painter.cpp b/src/mbgl/renderer/painter.cpp
deleted file mode 100644
index 47db8254e2..0000000000
--- a/src/mbgl/renderer/painter.cpp
+++ /dev/null
@@ -1,387 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_style.hpp>
-
-#include <mbgl/style/source.hpp>
-#include <mbgl/style/source_impl.hpp>
-
-#include <mbgl/map/view.hpp>
-
-#include <mbgl/util/logging.hpp>
-#include <mbgl/gl/debugging.hpp>
-
-#include <mbgl/style/layer_impl.hpp>
-#include <mbgl/style/layers/custom_layer_impl.hpp>
-
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/renderer/layers/render_background_layer.hpp>
-#include <mbgl/renderer/layers/render_custom_layer.hpp>
-#include <mbgl/style/layers/custom_layer_impl.hpp>
-#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
-
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-
-#include <mbgl/programs/program_parameters.hpp>
-#include <mbgl/programs/programs.hpp>
-
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/mat3.hpp>
-#include <mbgl/util/string.hpp>
-
-#include <mbgl/util/stopwatch.hpp>
-
-#include <cassert>
-#include <algorithm>
-#include <iostream>
-#include <unordered_set>
-
-namespace mbgl {
-
-using namespace style;
-
-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 }));
- return result;
-}
-
-static gl::IndexVector<gl::Triangles> quadTriangleIndices() {
- gl::IndexVector<gl::Triangles> result;
- result.emplace_back(0, 1, 2);
- result.emplace_back(1, 2, 3);
- return result;
-}
-
-static gl::IndexVector<gl::LineStrip> tileLineStripIndices() {
- gl::IndexVector<gl::LineStrip> result;
- result.emplace_back(0);
- result.emplace_back(1);
- result.emplace_back(3);
- result.emplace_back(2);
- result.emplace_back(0);
- return result;
-}
-
-static gl::VertexVector<RasterLayoutVertex> rasterVertices() {
- gl::VertexVector<RasterLayoutVertex> result;
- result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 }));
- result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { 32767, 0 }));
- result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, 32767 }));
- result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { 32767, 32767 }));
- return result;
-}
-
-static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() {
- gl::VertexVector<ExtrusionTextureLayoutVertex> result;
- result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 }));
- result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 }));
- result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 }));
- result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 }));
- return result;
-}
-
-
-Painter::Painter(gl::Context& context_,
- const TransformState& state_,
- float pixelRatio,
- const optional<std::string>& programCacheDir)
- : context(context_),
- state(state_),
- tileVertexBuffer(context.createVertexBuffer(tileVertices())),
- rasterVertexBuffer(context.createVertexBuffer(rasterVertices())),
- extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())),
- quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())),
- tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())) {
-
- tileTriangleSegments.emplace_back(0, 0, 4, 6);
- tileBorderSegments.emplace_back(0, 0, 4, 5);
- rasterSegments.emplace_back(0, 0, 4, 6);
- extrusionTextureSegments.emplace_back(0, 0, 4, 6);
-
- programs = std::make_unique<Programs>(context,
- ProgramParameters{ pixelRatio, false, programCacheDir });
-#ifndef NDEBUG
- overdrawPrograms =
- std::make_unique<Programs>(context, ProgramParameters{ pixelRatio, true, programCacheDir });
-#endif
-}
-
-Painter::~Painter() = default;
-
-bool Painter::needsAnimation() const {
- return frameHistory.needsAnimation(util::DEFAULT_TRANSITION_DURATION);
-}
-
-void Painter::cleanup() {
- context.performCleanup();
-}
-
-void Painter::render(RenderStyle& style, const FrameData& frame_, View& view) {
- frame = frame_;
- if (frame.contextMode == GLContextMode::Shared) {
- context.setDirtyState();
- }
-
- PaintParameters parameters {
-#ifndef NDEBUG
- paintMode() == PaintMode::Overdraw ? *overdrawPrograms : *programs,
-#else
- *programs,
-#endif
- view
- };
-
- imageManager = style.imageManager.get();
- lineAtlas = style.lineAtlas.get();
-
- evaluatedLight = style.getRenderLight().getEvaluated();
-
- RenderData renderData = style.getRenderData(frame.debugOptions, state.getAngle());
- const std::vector<RenderItem>& order = renderData.order;
- const std::unordered_set<RenderSource*>& sources = renderData.sources;
-
- // Update the default matrices to the current viewport dimensions.
- state.getProjMatrix(projMatrix);
- // 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.
- state.getProjMatrix(nearClippedProjMatrix, 100);
-
- pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }};
- if (state.getViewportMode() == ViewportMode::FlippedY) {
- pixelsToGLUnits[1] *= -1;
- }
-
- frameHistory.record(frame.timePoint, state.getZoom(),
- frame.mapMode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Milliseconds(0));
-
-
- // - UPLOAD PASS -------------------------------------------------------------------------------
- // Uploads all required buffers and images before we do any actual rendering.
- {
- MBGL_DEBUG_GROUP(context, "upload");
-
- imageManager->upload(context, 0);
- lineAtlas->upload(context, 0);
- frameHistory.upload(context, 0);
- }
-
- // - CLEAR -------------------------------------------------------------------------------------
- // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
- // tiles whatsoever.
- {
- MBGL_DEBUG_GROUP(context, "clear");
- view.bind();
- context.clear(paintMode() == PaintMode::Overdraw
- ? Color::black()
- : renderData.backgroundColor,
- 1.0f,
- 0);
- }
-
- // - CLIPPING MASKS ----------------------------------------------------------------------------
- // Draws the clipping masks to the stencil buffer.
- {
- MBGL_DEBUG_GROUP(context, "clip");
-
- // Update all clipping IDs.
- clipIDGenerator = algorithm::ClipIDGenerator();
- for (const auto& source : sources) {
- source->startRender(*this);
- }
-
- MBGL_DEBUG_GROUP(context, "clipping masks");
-
- for (const auto& stencil : clipIDGenerator.getStencils()) {
- MBGL_DEBUG_GROUP(context, std::string{ "mask: " } + util::toString(stencil.first));
- renderClippingMask(stencil.first, stencil.second);
- }
- }
-
-#if not MBGL_USE_GLES2 and not defined(NDEBUG)
- if (frame.debugOptions & MapDebugOptions::StencilClip) {
- renderClipMasks(parameters);
- return;
- }
-#endif
-
- // Actually render the layers
- if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
-
- depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon;
-
- // - OPAQUE PASS -------------------------------------------------------------------------------
- // Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
- renderPass(parameters,
- RenderPass::Opaque,
- order.rbegin(), order.rend(),
- 0, 1);
-
- // - TRANSLUCENT PASS --------------------------------------------------------------------------
- // Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
- renderPass(parameters,
- RenderPass::Translucent,
- order.begin(), order.end(),
- static_cast<uint32_t>(order.size()) - 1, -1);
-
- if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; }
-
- // - DEBUG PASS --------------------------------------------------------------------------------
- // Renders debug overlays.
- {
- MBGL_DEBUG_GROUP(context, "debug");
-
- // Finalize the rendering, e.g. by calling debug render calls per tile.
- // This guarantees that we have at least one function per tile called.
- // When only rendering layers via the stylesheet, it's possible that we don't
- // ever visit a tile during rendering.
- for (const auto& source : sources) {
- source->finishRender(*this);
- }
- }
-
-#if not MBGL_USE_GLES2 and not defined(NDEBUG)
- if (frame.debugOptions & MapDebugOptions::DepthBuffer) {
- renderDepthBuffer(parameters);
- }
-#endif
-
- // TODO: Find a better way to unbind VAOs after we're done with them without introducing
- // unnecessary bind(0)/bind(N) sequences.
- {
- MBGL_DEBUG_GROUP(context, "cleanup");
-
- context.activeTexture = 1;
- context.texture[1] = 0;
- context.activeTexture = 0;
- context.texture[0] = 0;
-
- context.vertexArrayObject = 0;
- }
-}
-
-template <class Iterator>
-void Painter::renderPass(PaintParameters& parameters,
- RenderPass pass_,
- Iterator it, Iterator end,
- uint32_t i, int8_t increment) {
- pass = pass_;
-
- MBGL_DEBUG_GROUP(context, pass == RenderPass::Opaque ? "opaque" : "translucent");
-
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s {", indent++ * 4, "",
- pass == RenderPass::Opaque ? "opaque" : "translucent");
- }
-
- for (; it != end; ++it, i += increment) {
- currentLayer = i;
-
- const auto& item = *it;
- const RenderLayer& layer = item.layer;
-
- if (!layer.hasRenderPass(pass))
- continue;
-
- if (layer.is<RenderBackgroundLayer>()) {
- MBGL_DEBUG_GROUP(context, "background");
- renderBackground(parameters, *layer.as<RenderBackgroundLayer>());
- } else if (layer.is<RenderFillExtrusionLayer>()) {
- const auto size = context.viewport.getCurrentValue().size;
-
- if (!extrusionTexture || extrusionTexture->getSize() != size) {
- extrusionTexture = OffscreenTexture(context, size, OffscreenTextureAttachment::Depth);
- }
-
- extrusionTexture->bind();
-
- context.setStencilMode(gl::StencilMode::disabled());
- context.setDepthMode(depthModeForSublayer(0, gl::DepthMode::ReadWrite));
- context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {});
-
- renderItem(parameters, item);
-
- parameters.view.bind();
- context.bindTexture(extrusionTexture->getTexture());
-
- mat4 viewportMat;
- matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
-
- const Properties<>::PossiblyEvaluated properties;
-
- parameters.programs.extrusionTexture.draw(
- context, gl::Triangles(), gl::DepthMode::disabled(), gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- ExtrusionTextureProgram::UniformValues{
- uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
- uniforms::u_image::Value{ 0 },
- uniforms::u_opacity::Value{ layer.as<RenderFillExtrusionLayer>()
- ->evaluated.get<FillExtrusionOpacity>() } },
- extrusionTextureVertexBuffer, quadTriangleIndexBuffer, extrusionTextureSegments,
- ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
- state.getZoom());
- } else {
- renderItem(parameters, item);
- }
- }
-
- if (debug::renderTree) {
- Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
- }
-}
-
-void Painter::renderItem(PaintParameters& parameters, const RenderItem& item) {
- RenderLayer& layer = item.layer;
- MBGL_DEBUG_GROUP(context, layer.getID());
- layer.render(*this, parameters, item.source);
-}
-
-mat4 Painter::matrixForTile(const UnwrappedTileID& tileID) {
- mat4 matrix;
- state.matrixFor(matrix, tileID);
- matrix::multiply(matrix, projMatrix, matrix);
- return matrix;
-}
-
-gl::DepthMode Painter::depthModeForSublayer(uint8_t n, gl::DepthMode::Mask mask) const {
- float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon;
- float farDepth = nearDepth + depthRangeSize;
- return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } };
-}
-
-gl::StencilMode Painter::stencilModeForClipping(const ClipID& id) const {
- return gl::StencilMode {
- gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) },
- static_cast<int32_t>(id.reference.to_ulong()),
- 0,
- gl::StencilMode::Keep,
- gl::StencilMode::Keep,
- gl::StencilMode::Replace
- };
-}
-
-gl::ColorMode Painter::colorModeForRenderPass() const {
- if (paintMode() == PaintMode::Overdraw) {
- const float overdraw = 1.0f / 8.0f;
- return gl::ColorMode {
- gl::ColorMode::Add {
- gl::ColorMode::ConstantColor,
- gl::ColorMode::One
- },
- Color { overdraw, overdraw, overdraw, 0.0f },
- gl::ColorMode::Mask { true, true, true, true }
- };
- } else if (pass == RenderPass::Translucent) {
- return gl::ColorMode::alphaBlended();
- } else {
- return gl::ColorMode::unblended();
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp
deleted file mode 100644
index 7e966adefa..0000000000
--- a/src/mbgl/renderer/painter.hpp
+++ /dev/null
@@ -1,181 +0,0 @@
-#pragma once
-
-#include <mbgl/map/transform_state.hpp>
-
-#include <mbgl/tile/tile_id.hpp>
-
-#include <mbgl/renderer/frame_history.hpp>
-#include <mbgl/renderer/render_item.hpp>
-#include <mbgl/renderer/bucket.hpp>
-#include <mbgl/renderer/render_light.hpp>
-
-#include <mbgl/gl/context.hpp>
-#include <mbgl/programs/debug_program.hpp>
-#include <mbgl/programs/program_parameters.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/programs/extrusion_texture_program.hpp>
-#include <mbgl/programs/raster_program.hpp>
-
-#include <mbgl/util/noncopyable.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/offscreen_texture.hpp>
-
-#include <mbgl/algorithm/generate_clip_ids.hpp>
-
-#include <array>
-#include <vector>
-#include <set>
-#include <map>
-
-namespace mbgl {
-
-class RenderStyle;
-class RenderTile;
-class ImageManager;
-class View;
-class LineAtlas;
-struct FrameData;
-class Tile;
-
-class DebugBucket;
-class FillBucket;
-class FillExtrusionBucket;
-class LineBucket;
-class CircleBucket;
-class SymbolBucket;
-class RasterBucket;
-
-class RenderFillLayer;
-class RenderFillExtrusionLayer;
-class RenderLineLayer;
-class RenderCircleLayer;
-class RenderSymbolLayer;
-class RenderRasterLayer;
-class RenderBackgroundLayer;
-
-class Programs;
-class PaintParameters;
-class TilePyramid;
-
-struct ClipID;
-
-struct FrameData {
- TimePoint timePoint;
- float pixelRatio;
- MapMode mapMode;
- GLContextMode contextMode;
- MapDebugOptions debugOptions;
-};
-
-class Painter : private util::noncopyable {
-public:
- Painter(gl::Context&, const TransformState&, float pixelRatio, const optional<std::string>& programCacheDir);
- ~Painter();
-
- void render(RenderStyle&,
- const FrameData&,
- View&);
-
- void cleanup();
-
- void renderClippingMask(const UnwrappedTileID&, const ClipID&);
- void renderTileDebug(const RenderTile&);
- void renderTileDebug(const mat4& matrix);
- void renderFill(PaintParameters&, FillBucket&, const RenderFillLayer&, const RenderTile&);
- void renderFillExtrusion(PaintParameters&, FillExtrusionBucket&, const RenderFillExtrusionLayer&, const RenderTile&);
- void renderLine(PaintParameters&, LineBucket&, const RenderLineLayer&, const RenderTile&);
- void renderCircle(PaintParameters&, CircleBucket&, const RenderCircleLayer&, const RenderTile&);
- void renderSymbol(PaintParameters&, SymbolBucket&, const RenderSymbolLayer&, const RenderTile&);
- void renderRaster(PaintParameters&, RasterBucket&, const RenderRasterLayer&, const mat4&, bool useBucketBuffers /* = false */);
- void renderBackground(PaintParameters&, const RenderBackgroundLayer&);
-
- void renderItem(PaintParameters&, const RenderItem&);
-
-#ifndef NDEBUG
- // Renders tile clip boundaries, using stencil buffer to calculate fill color.
- void renderClipMasks(PaintParameters&);
- // Renders the depth buffer.
- void renderDepthBuffer(PaintParameters&);
-#endif
-
- bool needsAnimation() const;
-
- template <class Iterator>
- void renderPass(PaintParameters&,
- RenderPass,
- Iterator it, Iterator end,
- uint32_t i, int8_t increment);
-
- mat4 matrixForTile(const UnwrappedTileID&);
- gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const;
- gl::StencilMode stencilModeForClipping(const ClipID&) const;
- gl::ColorMode colorModeForRenderPass() const;
-
-#ifndef NDEBUG
- PaintMode paintMode() const {
- return frame.debugOptions & MapDebugOptions::Overdraw ? PaintMode::Overdraw
- : PaintMode::Regular;
- }
-#else
- PaintMode paintMode() const {
- return PaintMode::Regular;
- }
-#endif
-
- gl::Context& context;
-
- algorithm::ClipIDGenerator clipIDGenerator;
-
- mat4 projMatrix;
- mat4 nearClippedProjMatrix;
-
- std::array<float, 2> pixelsToGLUnits;
-
- const mat4 identityMatrix = []{
- mat4 identity;
- matrix::identity(identity);
- return identity;
- }();
-
- const TransformState& state;
-
- FrameData frame;
-
- int indent = 0;
-
- RenderPass pass = RenderPass::Opaque;
-
- int numSublayers = 3;
- uint32_t currentLayer;
- float depthRangeSize;
- const float depthEpsilon = 1.0f / (1 << 16);
-
- ImageManager* imageManager = nullptr;
- LineAtlas* lineAtlas = nullptr;
-
- optional<OffscreenTexture> extrusionTexture;
-
- EvaluatedLight evaluatedLight;
-
- FrameHistory frameHistory;
-
- std::unique_ptr<Programs> programs;
-#ifndef NDEBUG
- std::unique_ptr<Programs> overdrawPrograms;
-#endif
-
- gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer;
- gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer;
- gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer;
-
- gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer;
- gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer;
-
- gl::SegmentVector<FillAttributes> tileTriangleSegments;
- gl::SegmentVector<DebugAttributes> tileBorderSegments;
- gl::SegmentVector<RasterAttributes> rasterSegments;
- gl::SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_background.cpp b/src/mbgl/renderer/painters/painter_background.cpp
deleted file mode 100644
index 577d7d6cda..0000000000
--- a/src/mbgl/renderer/painters/painter_background.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/layers/render_background_layer.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/util/tile_cover.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderBackground(PaintParameters& parameters, const RenderBackgroundLayer& layer) {
- // Note that for bottommost layers without a pattern, the background color is drawn with
- // glClear rather than this method.
- const BackgroundPaintProperties::PossiblyEvaluated& background = layer.evaluated;
-
- style::FillPaintProperties::PossiblyEvaluated properties;
- properties.get<FillPattern>() = background.get<BackgroundPattern>();
- properties.get<FillOpacity>() = { background.get<BackgroundOpacity>() };
- properties.get<FillColor>() = { background.get<BackgroundColor>() };
-
- const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
-
- if (!background.get<BackgroundPattern>().to.empty()) {
- optional<ImagePosition> imagePosA = imageManager->getPattern(background.get<BackgroundPattern>().from);
- optional<ImagePosition> imagePosB = imageManager->getPattern(background.get<BackgroundPattern>().to);
-
- if (!imagePosA || !imagePosB)
- return;
-
- imageManager->bind(context, 0);
-
- for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) {
- parameters.programs.fillPattern.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- FillPatternUniforms::values(
- matrixForTile(tileID),
- context.viewport.getCurrentValue().size,
- imageManager->getPixelSize(),
- *imagePosA,
- *imagePosB,
- background.get<BackgroundPattern>(),
- tileID,
- state
- ),
- tileVertexBuffer,
- quadTriangleIndexBuffer,
- tileTriangleSegments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
- }
- } else {
- for (const auto& tileID : util::tileCover(state, state.getIntegerZoom())) {
- parameters.programs.fill.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- FillProgram::UniformValues {
- uniforms::u_matrix::Value{ matrixForTile(tileID) },
- uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
- },
- tileVertexBuffer,
- quadTriangleIndexBuffer,
- tileTriangleSegments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
- }
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_circle.cpp b/src/mbgl/renderer/painters/painter_circle.cpp
deleted file mode 100644
index 58e384979d..0000000000
--- a/src/mbgl/renderer/painters/painter_circle.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/circle_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_circle_layer.hpp>
-#include <mbgl/style/layers/circle_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/circle_program.hpp>
-#include <mbgl/gl/context.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderCircle(PaintParameters& parameters,
- CircleBucket& bucket,
- const RenderCircleLayer& layer,
- const RenderTile& tile) {
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- const CirclePaintProperties::PossiblyEvaluated& properties = layer.evaluated;
- const bool scaleWithMap = properties.get<CirclePitchScale>() == CirclePitchScaleType::Map;
-
- parameters.programs.circle.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- frame.mapMode == MapMode::Still
- ? stencilModeForClipping(tile.clip)
- : gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- CircleProgram::UniformValues {
- uniforms::u_matrix::Value{
- tile.translatedMatrix(properties.get<CircleTranslate>(),
- properties.get<CircleTranslateAnchor>(),
- state)
- },
- uniforms::u_scale_with_map::Value{ scaleWithMap },
- uniforms::u_extrude_scale::Value{ scaleWithMap
- ? std::array<float, 2> {{
- pixelsToGLUnits[0] * state.getCameraToCenterDistance(),
- pixelsToGLUnits[1] * state.getCameraToCenterDistance()
- }}
- : pixelsToGLUnits }
- },
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom()
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_clipping.cpp b/src/mbgl/renderer/painters/painter_clipping.cpp
deleted file mode 100644
index cad092594e..0000000000
--- a/src/mbgl/renderer/painters/painter_clipping.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/util/clip_id.hpp>
-
-namespace mbgl {
-
-void Painter::renderClippingMask(const UnwrappedTileID& tileID, const ClipID& clip) {
- static const style::FillPaintProperties::PossiblyEvaluated properties {};
- static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
- programs->fill.get(properties).draw(
- context,
- gl::Triangles(),
- gl::DepthMode::disabled(),
- gl::StencilMode {
- gl::StencilMode::Always(),
- static_cast<int32_t>(clip.reference.to_ulong()),
- 0b11111111,
- gl::StencilMode::Keep,
- gl::StencilMode::Keep,
- gl::StencilMode::Replace
- },
- gl::ColorMode::disabled(),
- FillProgram::UniformValues {
- uniforms::u_matrix::Value{ matrixForTile(tileID) },
- uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
- },
- tileVertexBuffer,
- quadTriangleIndexBuffer,
- tileTriangleSegments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_debug.cpp b/src/mbgl/renderer/painters/painter_debug.cpp
deleted file mode 100644
index ee76aee54c..0000000000
--- a/src/mbgl/renderer/painters/painter_debug.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/buckets/debug_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/map/view.hpp>
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/util/string.hpp>
-#include <mbgl/gl/debugging.hpp>
-#include <mbgl/util/color.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderTileDebug(const RenderTile& renderTile) {
- if (frame.debugOptions == MapDebugOptions::NoDebug)
- return;
-
- MBGL_DEBUG_GROUP(context, std::string { "debug " } + util::toString(renderTile.id));
-
- static const style::Properties<>::PossiblyEvaluated properties {};
- static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
-
- auto draw = [&] (Color color, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments, auto drawMode) {
- programs->debug.draw(
- context,
- drawMode,
- gl::DepthMode::disabled(),
- stencilModeForClipping(renderTile.clip),
- gl::ColorMode::unblended(),
- DebugProgram::UniformValues {
- uniforms::u_matrix::Value{ renderTile.matrix },
- uniforms::u_color::Value{ color }
- },
- vertexBuffer,
- indexBuffer,
- segments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
- };
-
- if (frame.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
- Tile& tile = renderTile.tile;
- if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
- tile.debugBucket->complete != tile.isComplete() ||
- !(tile.debugBucket->modified == tile.modified) ||
- !(tile.debugBucket->expires == tile.expires) ||
- tile.debugBucket->debugMode != frame.debugOptions) {
- tile.debugBucket = std::make_unique<DebugBucket>(
- tile.id, tile.isRenderable(), tile.isComplete(), tile.modified,
- tile.expires, frame.debugOptions, context);
- }
-
- draw(Color::white(),
- *tile.debugBucket->vertexBuffer,
- *tile.debugBucket->indexBuffer,
- tile.debugBucket->segments,
- gl::Lines { 4.0f * frame.pixelRatio });
-
- draw(Color::black(),
- *tile.debugBucket->vertexBuffer,
- *tile.debugBucket->indexBuffer,
- tile.debugBucket->segments,
- gl::Lines { 2.0f * frame.pixelRatio });
- }
-
- if (frame.debugOptions & MapDebugOptions::TileBorders) {
- draw(Color::red(),
- tileVertexBuffer,
- tileBorderIndexBuffer,
- tileBorderSegments,
- gl::LineStrip { 4.0f * frame.pixelRatio });
- }
-}
-
-void Painter::renderTileDebug(const mat4& matrix) {
- if (frame.debugOptions == MapDebugOptions::NoDebug)
- return;
-
- static const style::Properties<>::PossiblyEvaluated properties {};
- static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
-
- if (frame.debugOptions & MapDebugOptions::TileBorders) {
- programs->debug.draw(
- context,
- gl::LineStrip { 4.0f * frame.pixelRatio },
- gl::DepthMode::disabled(),
- gl::StencilMode::disabled(),
- gl::ColorMode::unblended(),
- DebugProgram::UniformValues {
- uniforms::u_matrix::Value{ matrix },
- uniforms::u_color::Value{ Color::red() }
- },
- tileVertexBuffer,
- tileBorderIndexBuffer,
- tileBorderSegments,
- paintAttibuteData,
- properties,
- state.getZoom()
- );
- }
-}
-
-#ifndef NDEBUG
-void Painter::renderClipMasks(PaintParameters&) {
- context.setStencilMode(gl::StencilMode::disabled());
- context.setDepthMode(gl::DepthMode::disabled());
- context.setColorMode(gl::ColorMode::unblended());
- context.program = 0;
-
-#if not MBGL_USE_GLES2
- // Reset the value in case someone else changed it, or it's dirty.
- context.pixelTransferStencil = gl::value::PixelTransferStencil::Default;
-
- // Read the stencil buffer
- const auto viewport = context.viewport.getCurrentValue();
- auto image =
- context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false);
-
- // Scale the Stencil buffer to cover the entire color space.
- auto it = image.data.get();
- auto end = it + viewport.size.width * viewport.size.height;
- const auto factor = 255.0f / *std::max_element(it, end);
- for (; it != end; ++it) {
- *it *= factor;
- }
-
- context.pixelZoom = { 1, 1 };
- context.rasterPos = { -1, -1, 0, 1 };
- context.drawPixels(image);
-#endif // MBGL_USE_GLES2
-}
-
-void Painter::renderDepthBuffer(PaintParameters&) {
- context.setStencilMode(gl::StencilMode::disabled());
- context.setDepthMode(gl::DepthMode::disabled());
- context.setColorMode(gl::ColorMode::unblended());
- context.program = 0;
-
-#if not MBGL_USE_GLES2
- // Scales the values in the depth buffer so that they cover the entire grayscale range. This
- // makes it easier to spot tiny differences.
- const float base = 1.0f / (1.0f - depthRangeSize);
- context.pixelTransferDepth = { base, 1.0f - base };
-
- // Read the stencil buffer
- auto viewport = context.viewport.getCurrentValue();
- auto image =
- context.readFramebuffer<AlphaImage, gl::TextureFormat::Depth>(viewport.size, false);
-
- context.pixelZoom = { 1, 1 };
- context.rasterPos = { -1, -1, 0, 1 };
- context.drawPixels(image);
-#endif // MBGL_USE_GLES2
-}
-#endif // NDEBUG
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_fill.cpp b/src/mbgl/renderer/painters/painter_fill.cpp
deleted file mode 100644
index 3a0bfed454..0000000000
--- a/src/mbgl/renderer/painters/painter_fill.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/fill_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_fill_layer.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/layers/fill_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_program.hpp>
-#include <mbgl/util/convert.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderFill(PaintParameters& parameters,
- FillBucket& bucket,
- const RenderFillLayer& layer,
- const RenderTile& tile) {
- const FillPaintProperties::PossiblyEvaluated& properties = layer.evaluated;
-
- if (!properties.get<FillPattern>().from.empty()) {
- if (pass != RenderPass::Translucent) {
- return;
- }
-
- optional<ImagePosition> imagePosA = imageManager->getPattern(properties.get<FillPattern>().from);
- optional<ImagePosition> imagePosB = imageManager->getPattern(properties.get<FillPattern>().to);
-
- if (!imagePosA || !imagePosB) {
- return;
- }
-
- imageManager->bind(context, 0);
-
- auto draw = [&] (uint8_t sublayer,
- auto& program,
- const auto& drawMode,
- const auto& indexBuffer,
- const auto& segments) {
- program.get(properties).draw(
- context,
- drawMode,
- depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite),
- stencilModeForClipping(tile.clip),
- colorModeForRenderPass(),
- FillPatternUniforms::values(
- tile.translatedMatrix(properties.get<FillTranslate>(),
- properties.get<FillTranslateAnchor>(),
- state),
- context.viewport.getCurrentValue().size,
- imageManager->getPixelSize(),
- *imagePosA,
- *imagePosB,
- properties.get<FillPattern>(),
- tile.id,
- state
- ),
- *bucket.vertexBuffer,
- indexBuffer,
- segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom()
- );
- };
-
- draw(0,
- parameters.programs.fillPattern,
- gl::Triangles(),
- *bucket.triangleIndexBuffer,
- bucket.triangleSegments);
-
- if (!properties.get<FillAntialias>() || !layer.unevaluated.get<FillOutlineColor>().isUndefined()) {
- return;
- }
-
- draw(2,
- parameters.programs.fillOutlinePattern,
- gl::Lines { 2.0f },
- *bucket.lineIndexBuffer,
- bucket.lineSegments);
- } else {
- auto draw = [&] (uint8_t sublayer,
- auto& program,
- const auto& drawMode,
- const auto& indexBuffer,
- const auto& segments) {
- program.get(properties).draw(
- context,
- drawMode,
- depthModeForSublayer(sublayer, gl::DepthMode::ReadWrite),
- stencilModeForClipping(tile.clip),
- colorModeForRenderPass(),
- FillProgram::UniformValues {
- uniforms::u_matrix::Value{
- tile.translatedMatrix(properties.get<FillTranslate>(),
- properties.get<FillTranslateAnchor>(),
- state)
- },
- uniforms::u_world::Value{ context.viewport.getCurrentValue().size },
- },
- *bucket.vertexBuffer,
- indexBuffer,
- segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom()
- );
- };
-
- if (properties.get<FillAntialias>() && !layer.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) {
- draw(2,
- parameters.programs.fillOutline,
- gl::Lines { 2.0f },
- *bucket.lineIndexBuffer,
- bucket.lineSegments);
- }
-
- // Only draw the fill when it's opaque and we're drawing opaque fragments,
- // or when it's translucent and we're drawing translucent fragments.
- if ((properties.get<FillColor>().constantOr(Color()).a >= 1.0f
- && properties.get<FillOpacity>().constantOr(0) >= 1.0f) == (pass == RenderPass::Opaque)) {
- draw(1,
- parameters.programs.fill,
- gl::Triangles(),
- *bucket.triangleIndexBuffer,
- bucket.triangleSegments);
- }
-
- if (properties.get<FillAntialias>() && layer.unevaluated.get<FillOutlineColor>().isUndefined() && pass == RenderPass::Translucent) {
- draw(2,
- parameters.programs.fillOutline,
- gl::Lines { 2.0f },
- *bucket.lineIndexBuffer,
- bucket.lineSegments);
- }
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_fill_extrusion.cpp b/src/mbgl/renderer/painters/painter_fill_extrusion.cpp
deleted file mode 100644
index 165476944b..0000000000
--- a/src/mbgl/renderer/painters/painter_fill_extrusion.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/fill_extrusion_program.hpp>
-#include <mbgl/util/constants.hpp>
-#include <mbgl/util/convert.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderFillExtrusion(PaintParameters& parameters,
- FillExtrusionBucket& bucket,
- const RenderFillExtrusionLayer& layer,
- const RenderTile& tile) {
- const FillExtrusionPaintProperties::PossiblyEvaluated& properties = layer.evaluated;
-
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- if (!properties.get<FillExtrusionPattern>().from.empty()) {
- optional<ImagePosition> imagePosA = imageManager->getPattern(properties.get<FillExtrusionPattern>().from);
- optional<ImagePosition> imagePosB = imageManager->getPattern(properties.get<FillExtrusionPattern>().to);
-
- if (!imagePosA || !imagePosB) {
- return;
- }
-
- imageManager->bind(context, 0);
-
- parameters.programs.fillExtrusionPattern.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadWrite),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- FillExtrusionPatternUniforms::values(
- tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(),
- properties.get<FillExtrusionTranslateAnchor>(),
- state),
- imageManager->getPixelSize(),
- *imagePosA,
- *imagePosB,
- properties.get<FillExtrusionPattern>(),
- tile.id,
- state,
- -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
- evaluatedLight
- ),
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.triangleSegments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom());
-
- } else {
- parameters.programs.fillExtrusion.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadWrite),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- FillExtrusionUniforms::values(
- tile.translatedClipMatrix(properties.get<FillExtrusionTranslate>(),
- properties.get<FillExtrusionTranslateAnchor>(),
- state),
- state,
- evaluatedLight
- ),
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.triangleSegments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom());
- };
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_line.cpp b/src/mbgl/renderer/painters/painter_line.cpp
deleted file mode 100644
index 58f4131d96..0000000000
--- a/src/mbgl/renderer/painters/painter_line.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/line_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_line_layer.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/layers/line_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/line_program.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderLine(PaintParameters& parameters,
- LineBucket& bucket,
- const RenderLineLayer& layer,
- const RenderTile& tile) {
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- const RenderLinePaintProperties::PossiblyEvaluated& properties = layer.evaluated;
-
- auto draw = [&] (auto& program, auto&& uniformValues) {
- program.get(properties).draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- stencilModeForClipping(tile.clip),
- colorModeForRenderPass(),
- std::move(uniformValues),
- *bucket.vertexBuffer,
- *bucket.indexBuffer,
- bucket.segments,
- bucket.paintPropertyBinders.at(layer.getID()),
- properties,
- state.getZoom()
- );
- };
-
- if (!properties.get<LineDasharray>().from.empty()) {
- const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round
- ? LinePatternCap::Round : LinePatternCap::Square;
- LinePatternPos posA = lineAtlas->getDashPosition(properties.get<LineDasharray>().from, cap);
- LinePatternPos posB = lineAtlas->getDashPosition(properties.get<LineDasharray>().to, cap);
-
- lineAtlas->bind(context, 0);
-
- draw(parameters.programs.lineSDF,
- LineSDFProgram::uniformValues(
- properties,
- frame.pixelRatio,
- tile,
- state,
- pixelsToGLUnits,
- posA,
- posB,
- lineAtlas->getSize().width));
-
- } else if (!properties.get<LinePattern>().from.empty()) {
- optional<ImagePosition> posA = imageManager->getPattern(properties.get<LinePattern>().from);
- optional<ImagePosition> posB = imageManager->getPattern(properties.get<LinePattern>().to);
-
- if (!posA || !posB)
- return;
-
- imageManager->bind(context, 0);
-
- draw(parameters.programs.linePattern,
- LinePatternProgram::uniformValues(
- properties,
- tile,
- state,
- pixelsToGLUnits,
- imageManager->getPixelSize(),
- *posA,
- *posB));
-
- } else {
- draw(parameters.programs.line,
- LineProgram::uniformValues(
- properties,
- tile,
- state,
- pixelsToGLUnits));
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_raster.cpp b/src/mbgl/renderer/painters/painter_raster.cpp
deleted file mode 100644
index 56e38ae8f4..0000000000
--- a/src/mbgl/renderer/painters/painter_raster.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/buckets/raster_bucket.hpp>
-#include <mbgl/renderer/layers/render_raster_layer.hpp>
-#include <mbgl/style/layers/raster_layer_impl.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/raster_program.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-static float saturationFactor(float saturation) {
- if (saturation > 0) {
- return 1 - 1 / (1.001 - saturation);
- } else {
- return -saturation;
- }
-}
-
-static float contrastFactor(float contrast) {
- if (contrast > 0) {
- return 1 / (1 - contrast);
- } else {
- return 1 + contrast;
- }
-}
-
-static std::array<float, 3> spinWeights(float spin) {
- spin *= util::DEG2RAD;
- float s = std::sin(spin);
- float c = std::cos(spin);
- std::array<float, 3> spin_weights = {{
- (2 * c + 1) / 3,
- (-std::sqrt(3.0f) * s - c + 1) / 3,
- (std::sqrt(3.0f) * s - c + 1) / 3
- }};
- return spin_weights;
-}
-
-void Painter::renderRaster(PaintParameters& parameters,
- RasterBucket& bucket,
- const RenderRasterLayer& layer,
- const mat4& matrix,
- bool useBucketBuffers = false) {
- if (pass != RenderPass::Translucent)
- return;
- if (!bucket.hasData())
- return;
-
- const RasterPaintProperties::PossiblyEvaluated& properties = layer.evaluated;
- const RasterProgram::PaintPropertyBinders paintAttributeData(properties, 0);
-
- assert(bucket.texture);
- context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear);
- context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear);
-
- parameters.programs.raster.draw(
- context,
- gl::Triangles(),
- depthModeForSublayer(0, gl::DepthMode::ReadOnly),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- RasterProgram::UniformValues {
- uniforms::u_matrix::Value{ matrix },
- uniforms::u_image0::Value{ 0 },
- uniforms::u_image1::Value{ 1 },
- uniforms::u_opacity::Value{ properties.get<RasterOpacity>() },
- uniforms::u_fade_t::Value{ 1 },
- uniforms::u_brightness_low::Value{ properties.get<RasterBrightnessMin>() },
- uniforms::u_brightness_high::Value{ properties.get<RasterBrightnessMax>() },
- uniforms::u_saturation_factor::Value{ saturationFactor(properties.get<RasterSaturation>()) },
- uniforms::u_contrast_factor::Value{ contrastFactor(properties.get<RasterContrast>()) },
- uniforms::u_spin_weights::Value{ spinWeights(properties.get<RasterHueRotate>()) },
- uniforms::u_buffer_scale::Value{ 1.0f },
- uniforms::u_scale_parent::Value{ 1.0f },
- uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} },
- },
- useBucketBuffers ? *bucket.vertexBuffer : rasterVertexBuffer,
- useBucketBuffers ? *bucket.indexBuffer : quadTriangleIndexBuffer,
- useBucketBuffers ? bucket.segments : rasterSegments,
- paintAttributeData,
- properties,
- state.getZoom()
- );
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/painters/painter_symbol.cpp b/src/mbgl/renderer/painters/painter_symbol.cpp
deleted file mode 100644
index d3a505aa3f..0000000000
--- a/src/mbgl/renderer/painters/painter_symbol.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-#include <mbgl/renderer/painter.hpp>
-#include <mbgl/renderer/paint_parameters.hpp>
-#include <mbgl/renderer/buckets/symbol_bucket.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/style/layers/symbol_layer_impl.hpp>
-#include <mbgl/text/glyph_atlas.hpp>
-#include <mbgl/programs/programs.hpp>
-#include <mbgl/programs/symbol_program.hpp>
-#include <mbgl/programs/collision_box_program.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/tile/geometry_tile.hpp>
-
-#include <cmath>
-
-namespace mbgl {
-
-using namespace style;
-
-void Painter::renderSymbol(PaintParameters& parameters,
- SymbolBucket& bucket,
- const RenderSymbolLayer& layer,
- const RenderTile& tile) {
- if (pass == RenderPass::Opaque) {
- return;
- }
-
- const auto& layout = bucket.layout;
-
- frameHistory.bind(context, 1);
-
- auto draw = [&] (auto& program,
- auto&& uniformValues,
- const auto& buffers,
- const auto& symbolSizeBinder,
- const SymbolPropertyValues& values_,
- const auto& binders,
- const auto& paintProperties)
- {
- // We clip symbols to their tile extent in still mode.
- const bool needsClipping = frame.mapMode == MapMode::Still;
-
- program.get(paintProperties).draw(
- context,
- gl::Triangles(),
- values_.pitchAlignment == AlignmentType::Map
- ? depthModeForSublayer(0, gl::DepthMode::ReadOnly)
- : gl::DepthMode::disabled(),
- needsClipping
- ? stencilModeForClipping(tile.clip)
- : gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- std::move(uniformValues),
- *buffers.vertexBuffer,
- *symbolSizeBinder,
- *buffers.indexBuffer,
- buffers.segments,
- binders,
- paintProperties,
- state.getZoom()
- );
- };
-
- assert(dynamic_cast<GeometryTile*>(&tile.tile));
- GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile);
-
- if (bucket.hasIconData()) {
- auto values = layer.iconPropertyValues(layout);
- auto paintPropertyValues = layer.iconPaintProperties();
-
- const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear;
- const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0;
-
- context.bindTexture(*geometryTile.iconAtlasTexture, 0,
- bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed
- ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest);
-
- const Size texsize = geometryTile.iconAtlasTexture->size;
-
- if (bucket.sdfIcons) {
- if (values.hasHalo) {
- draw(parameters.programs.symbolIconSDF,
- SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo),
- bucket.icon,
- bucket.iconSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).first,
- paintPropertyValues);
- }
-
- if (values.hasFill) {
- draw(parameters.programs.symbolIconSDF,
- SymbolSDFIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill),
- bucket.icon,
- bucket.iconSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).first,
- paintPropertyValues);
- }
- } else {
- draw(parameters.programs.symbolIcon,
- SymbolIconProgram::uniformValues(false, values, texsize, pixelsToGLUnits, tile, state),
- bucket.icon,
- bucket.iconSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).first,
- paintPropertyValues);
- }
- }
-
- if (bucket.hasTextData()) {
- context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear);
-
- auto values = layer.textPropertyValues(layout);
- auto paintPropertyValues = layer.textPaintProperties();
-
- const Size texsize = geometryTile.glyphAtlasTexture->size;
-
- if (values.hasHalo) {
- draw(parameters.programs.symbolGlyph,
- SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo),
- bucket.text,
- bucket.textSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).second,
- paintPropertyValues);
- }
-
- if (values.hasFill) {
- draw(parameters.programs.symbolGlyph,
- SymbolSDFTextProgram::uniformValues(true, values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill),
- bucket.text,
- bucket.textSizeBinder,
- values,
- bucket.paintPropertyBinders.at(layer.getID()).second,
- paintPropertyValues);
- }
- }
-
- if (bucket.hasCollisionBoxData()) {
- static const style::Properties<>::PossiblyEvaluated properties {};
- static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0);
-
- programs->collisionBox.draw(
- context,
- gl::Lines { 1.0f },
- gl::DepthMode::disabled(),
- gl::StencilMode::disabled(),
- colorModeForRenderPass(),
- CollisionBoxProgram::UniformValues {
- uniforms::u_matrix::Value{ tile.matrix },
- uniforms::u_scale::Value{ std::pow(2.0f, float(state.getZoom() - tile.tile.id.overscaledZ)) },
- uniforms::u_zoom::Value{ float(state.getZoom() * 10) },
- uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) },
- },
- *bucket.collisionBox.vertexBuffer,
- *bucket.collisionBox.indexBuffer,
- bucket.collisionBox.segments,
- paintAttributeData,
- properties,
- state.getZoom()
- );
- }
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/possibly_evaluated_property_value.hpp b/src/mbgl/renderer/possibly_evaluated_property_value.hpp
index 8a5dfbe4ea..e662d5dfb1 100644
--- a/src/mbgl/renderer/possibly_evaluated_property_value.hpp
+++ b/src/mbgl/renderer/possibly_evaluated_property_value.hpp
@@ -45,7 +45,7 @@ public:
template <class Feature>
T evaluate(const Feature& feature, float zoom, T defaultValue) const {
return this->match(
- [&] (const T& constant) { return constant; },
+ [&] (const T& constant_) { return constant_; },
[&] (const style::SourceFunction<T>& function) {
return function.evaluate(feature, defaultValue);
},
diff --git a/src/mbgl/renderer/render_item.hpp b/src/mbgl/renderer/render_item.hpp
deleted file mode 100644
index 4bf5629263..0000000000
--- a/src/mbgl/renderer/render_item.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include <mbgl/util/color.hpp>
-
-#include <unordered_set>
-#include <vector>
-
-namespace mbgl {
-
-class RenderLayer;
-class RenderSource;
-class RenderTile;
-class Bucket;
-
-namespace style {
-} // namespace style
-
-class RenderItem {
-public:
- RenderItem(RenderLayer& layer_,
- RenderSource* renderSource_)
- : layer(layer_), source(renderSource_) {
- }
-
- RenderLayer& layer;
- RenderSource* source;
-};
-
-class RenderData {
-public:
- Color backgroundColor;
- std::unordered_set<RenderSource*> sources;
- std::vector<RenderItem> order;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp
index 3f9d68003e..eb2b74ffe0 100644
--- a/src/mbgl/renderer/render_layer.cpp
+++ b/src/mbgl/renderer/render_layer.cpp
@@ -9,7 +9,6 @@
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/style/types.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/tile/tile.hpp>
namespace mbgl {
@@ -68,14 +67,5 @@ void RenderLayer::setRenderTiles(std::vector<std::reference_wrapper<RenderTile>>
renderTiles = std::move(tiles);
}
-void RenderLayer::render(Painter& painter, PaintParameters& parameters, RenderSource*) {
- for (auto& tileRef : renderTiles) {
- auto& tile = tileRef.get();
- auto bucket = tile.tile.getBucket(*baseImpl);
- bucket->render(painter, parameters, *this, tile);
- }
-}
-
-
} //namespace mbgl
diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp
index e06f479281..55831cb72c 100644
--- a/src/mbgl/renderer/render_layer.hpp
+++ b/src/mbgl/renderer/render_layer.hpp
@@ -14,7 +14,6 @@ class Bucket;
class BucketParameters;
class TransitionParameters;
class PropertyEvaluationParameters;
-class Painter;
class PaintParameters;
class RenderSource;
class RenderTile;
@@ -62,7 +61,7 @@ public:
// Checks whether this layer can be rendered.
bool needsRendering(float zoom) const;
- virtual void render(Painter&, PaintParameters&, RenderSource*);
+ virtual void render(PaintParameters&, RenderSource*) = 0;
// Check wether the given geometry intersects
// with the feature
@@ -83,13 +82,16 @@ public:
friend std::string layoutKey(const RenderLayer&);
protected:
+ // renderTiles are exposed directly to CrossTileSymbolIndex and Placement so they
+ // can update opacities in the symbol buckets immediately before rendering
+ friend class CrossTileSymbolIndex;
+ friend class Placement;
+ // Stores current set of tiles to be rendered for this layer.
+ std::vector<std::reference_wrapper<RenderTile>> renderTiles;
+
// Stores what render passes this layer is currently enabled for. This depends on the
// evaluated StyleProperties object and is updated accordingly.
RenderPass passes = RenderPass::None;
-
- //Stores current set of tiles to be rendered for this layer.
- std::vector<std::reference_wrapper<RenderTile>> renderTiles;
-
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_pass.hpp b/src/mbgl/renderer/render_pass.hpp
index d273e34ff7..5d18304129 100644
--- a/src/mbgl/renderer/render_pass.hpp
+++ b/src/mbgl/renderer/render_pass.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <mbgl/util/traits.hpp>
+#include <mbgl/util/util.hpp>
#include <cstdint>
@@ -10,17 +11,18 @@ enum class RenderPass : uint8_t {
None = 0,
Opaque = 1 << 0,
Translucent = 1 << 1,
+ Pass3D = 1 << 2,
};
-constexpr RenderPass operator|(RenderPass a, RenderPass b) {
+MBGL_CONSTEXPR RenderPass operator|(RenderPass a, RenderPass b) {
return RenderPass(mbgl::underlying_type(a) | mbgl::underlying_type(b));
}
-constexpr RenderPass& operator|=(RenderPass& a, RenderPass b) {
+MBGL_CONSTEXPR RenderPass& operator|=(RenderPass& a, RenderPass b) {
return (a = a | b);
}
-constexpr RenderPass operator&(RenderPass a, RenderPass b) {
+MBGL_CONSTEXPR RenderPass operator&(RenderPass a, RenderPass b) {
return RenderPass(mbgl::underlying_type(a) & mbgl::underlying_type(b));
}
diff --git a/src/mbgl/renderer/render_source.cpp b/src/mbgl/renderer/render_source.cpp
index 7723a1c7ca..6624bb7d96 100644
--- a/src/mbgl/renderer/render_source.cpp
+++ b/src/mbgl/renderer/render_source.cpp
@@ -6,6 +6,7 @@
#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 {
@@ -27,6 +28,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 a00a6c797d..8c84af4f1e 100644
--- a/src/mbgl/renderer/render_source.hpp
+++ b/src/mbgl/renderer/render_source.hpp
@@ -15,16 +15,16 @@
namespace mbgl {
-class Painter;
+class PaintParameters;
class TransformState;
class RenderTile;
-class RenderStyle;
class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
class Tile;
class RenderSourceObserver;
class TileParameters;
+class CollisionIndex;
class RenderSource : protected TileObserver {
public:
@@ -54,21 +54,22 @@ public:
bool needsRelayout,
const TileParameters&) = 0;
- virtual void startRender(Painter&) = 0;
- virtual void finishRender(Painter&) = 0;
+ virtual void startRender(PaintParameters&) = 0;
+ virtual void finishRender(PaintParameters&) = 0;
- virtual std::map<UnwrappedTileID, RenderTile>& getRenderTiles() = 0;
+ // Returns an unsorted list of RenderTiles.
+ virtual std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() = 0;
virtual std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const = 0;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const = 0;
virtual std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const = 0;
- virtual void setCacheSize(size_t) = 0;
virtual void onLowMemory() = 0;
virtual void dumpDebugLogs() const = 0;
diff --git a/src/mbgl/renderer/render_static_data.cpp b/src/mbgl/renderer/render_static_data.cpp
new file mode 100644
index 0000000000..ccf239e643
--- /dev/null
+++ b/src/mbgl/renderer/render_static_data.cpp
@@ -0,0 +1,67 @@
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/program_parameters.hpp>
+
+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 }));
+ return result;
+}
+
+static gl::IndexVector<gl::Triangles> quadTriangleIndices() {
+ gl::IndexVector<gl::Triangles> result;
+ result.emplace_back(0, 1, 2);
+ result.emplace_back(1, 2, 3);
+ return result;
+}
+
+static gl::IndexVector<gl::LineStrip> tileLineStripIndices() {
+ gl::IndexVector<gl::LineStrip> result;
+ result.emplace_back(0);
+ result.emplace_back(1);
+ result.emplace_back(3);
+ result.emplace_back(2);
+ result.emplace_back(0);
+ return result;
+}
+
+static gl::VertexVector<RasterLayoutVertex> rasterVertices() {
+ gl::VertexVector<RasterLayoutVertex> result;
+ result.emplace_back(RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 }));
+ result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, 0 }, { util::EXTENT, 0 }));
+ result.emplace_back(RasterProgram::layoutVertex({ 0, util::EXTENT }, { 0, util::EXTENT }));
+ result.emplace_back(RasterProgram::layoutVertex({ util::EXTENT, util::EXTENT }, { util::EXTENT, util::EXTENT }));
+ return result;
+}
+
+static gl::VertexVector<ExtrusionTextureLayoutVertex> extrusionTextureVertices() {
+ gl::VertexVector<ExtrusionTextureLayoutVertex> result;
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 0 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 0 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 0, 1 }));
+ result.emplace_back(ExtrusionTextureProgram::layoutVertex({ 1, 1 }));
+ return result;
+}
+
+RenderStaticData::RenderStaticData(gl::Context& context, float pixelRatio, const optional<std::string>& programCacheDir)
+ : tileVertexBuffer(context.createVertexBuffer(tileVertices())),
+ rasterVertexBuffer(context.createVertexBuffer(rasterVertices())),
+ extrusionTextureVertexBuffer(context.createVertexBuffer(extrusionTextureVertices())),
+ quadTriangleIndexBuffer(context.createIndexBuffer(quadTriangleIndices())),
+ tileBorderIndexBuffer(context.createIndexBuffer(tileLineStripIndices())),
+ programs(context, ProgramParameters { pixelRatio, false, programCacheDir })
+#ifndef NDEBUG
+ , overdrawPrograms(context, ProgramParameters { pixelRatio, true, programCacheDir })
+#endif
+{
+ tileTriangleSegments.emplace_back(0, 0, 4, 6);
+ tileBorderSegments.emplace_back(0, 0, 4, 5);
+ rasterSegments.emplace_back(0, 0, 4, 6);
+ extrusionTextureSegments.emplace_back(0, 0, 4, 6);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_static_data.hpp b/src/mbgl/renderer/render_static_data.hpp
new file mode 100644
index 0000000000..cf58c31f4d
--- /dev/null
+++ b/src/mbgl/renderer/render_static_data.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/gl/vertex_buffer.hpp>
+#include <mbgl/gl/index_buffer.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <string>
+
+namespace mbgl {
+
+class RenderStaticData {
+public:
+ RenderStaticData(gl::Context&, float pixelRatio, const optional<std::string>& programCacheDir);
+
+ gl::VertexBuffer<FillLayoutVertex> tileVertexBuffer;
+ gl::VertexBuffer<RasterLayoutVertex> rasterVertexBuffer;
+ gl::VertexBuffer<ExtrusionTextureLayoutVertex> extrusionTextureVertexBuffer;
+
+ gl::IndexBuffer<gl::Triangles> quadTriangleIndexBuffer;
+ gl::IndexBuffer<gl::LineStrip> tileBorderIndexBuffer;
+
+ SegmentVector<FillAttributes> tileTriangleSegments;
+ SegmentVector<DebugAttributes> tileBorderSegments;
+ SegmentVector<RasterAttributes> rasterSegments;
+ SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
+
+ optional<gl::Renderbuffer<gl::RenderbufferType::DepthComponent>> depthRenderbuffer;
+ bool has3D = false;
+ Size backendSize;
+
+ Programs programs;
+
+#ifndef NDEBUG
+ Programs overdrawPrograms;
+#endif
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style.cpp b/src/mbgl/renderer/render_style.cpp
deleted file mode 100644
index f9c3f0ca9f..0000000000
--- a/src/mbgl/renderer/render_style.cpp
+++ /dev/null
@@ -1,456 +0,0 @@
-#include <mbgl/renderer/render_style.hpp>
-#include <mbgl/renderer/render_style_observer.hpp>
-#include <mbgl/renderer/update_parameters.hpp>
-#include <mbgl/renderer/transition_parameters.hpp>
-#include <mbgl/renderer/property_evaluation_parameters.hpp>
-#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_item.hpp>
-#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/layers/render_background_layer.hpp>
-#include <mbgl/renderer/layers/render_circle_layer.hpp>
-#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_line_layer.hpp>
-#include <mbgl/renderer/layers/render_raster_layer.hpp>
-#include <mbgl/renderer/layers/render_symbol_layer.hpp>
-#include <mbgl/renderer/style_diff.hpp>
-#include <mbgl/renderer/image_manager.hpp>
-#include <mbgl/style/style.hpp>
-#include <mbgl/style/source_impl.hpp>
-#include <mbgl/style/transition_options.hpp>
-#include <mbgl/sprite/sprite_loader.hpp>
-#include <mbgl/text/glyph_manager.hpp>
-#include <mbgl/geometry/line_atlas.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/map/query.hpp>
-#include <mbgl/tile/tile.hpp>
-#include <mbgl/util/math.hpp>
-#include <mbgl/util/string.hpp>
-
-namespace mbgl {
-
-using namespace style;
-
-RenderStyleObserver nullObserver;
-
-RenderStyle::RenderStyle(Scheduler& scheduler_, FileSource& fileSource_)
- : scheduler(scheduler_),
- fileSource(fileSource_),
- glyphManager(std::make_unique<GlyphManager>(fileSource)),
- imageManager(std::make_unique<ImageManager>()),
- lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 })),
- 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>()),
- observer(&nullObserver) {
- glyphManager->setObserver(this);
-}
-
-RenderStyle::~RenderStyle() {
- assert(BackendScope::exists()); // Required for custom layers.
-}
-
-void RenderStyle::setObserver(RenderStyleObserver* observer_) {
- observer = observer_;
-}
-
-std::vector<const RenderLayer*> RenderStyle::getRenderLayers() const {
- std::vector<const RenderLayer*> result;
- result.reserve(renderLayers.size());
- for (const auto& layer : *layerImpls) {
- result.push_back(getRenderLayer(layer->id));
- }
- return result;
-}
-
-RenderLayer* RenderStyle::getRenderLayer(const std::string& id) {
- auto it = renderLayers.find(id);
- return it != renderLayers.end() ? it->second.get() : nullptr;
-}
-
-const RenderLayer* RenderStyle::getRenderLayer(const std::string& id) const {
- auto it = renderLayers.find(id);
- return it != renderLayers.end() ? it->second.get() : nullptr;
-}
-
-const RenderLight& RenderStyle::getRenderLight() const {
- return renderLight;
-}
-
-void RenderStyle::update(const UpdateParameters& parameters) {
- assert(BackendScope::exists()); // Required for custom layers.
-
- const bool zoomChanged = zoomHistory.update(parameters.transformState.getZoom(), parameters.timePoint);
-
- const TransitionParameters transitionParameters {
- parameters.mode == MapMode::Continuous ? parameters.timePoint : Clock::time_point::max(),
- parameters.mode == MapMode::Continuous ? parameters.transitionOptions : TransitionOptions()
- };
-
- const PropertyEvaluationParameters evaluationParameters {
- zoomHistory,
- parameters.mode == MapMode::Continuous ? parameters.timePoint : Clock::time_point::max(),
- parameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero()
- };
-
- const TileParameters tileParameters {
- parameters.pixelRatio,
- parameters.debugOptions,
- parameters.transformState,
- parameters.scheduler,
- parameters.fileSource,
- parameters.mode,
- parameters.annotationManager,
- *imageManager,
- *glyphManager
- };
-
- glyphManager->setURL(parameters.glyphURL);
-
- // Update light.
- const bool lightChanged = renderLight.impl != parameters.light;
-
- if (lightChanged) {
- renderLight.impl = parameters.light;
- renderLight.transition(transitionParameters);
- }
-
- if (lightChanged || zoomChanged || renderLight.hasTransition()) {
- renderLight.evaluate(evaluationParameters);
- }
-
-
- const ImageDifference imageDiff = diffImages(imageImpls, parameters.images);
- imageImpls = parameters.images;
-
- // Remove removed images from sprite atlas.
- for (const auto& entry : imageDiff.removed) {
- imageManager->removeImage(entry.first);
- }
-
- // Add added images to sprite atlas.
- for (const auto& entry : imageDiff.added) {
- imageManager->addImage(entry.second);
- }
-
- // Update changed images.
- for (const auto& entry : imageDiff.changed) {
- imageManager->updateImage(entry.second.after);
- }
-
- if (parameters.spriteLoaded && !imageManager->isLoaded()) {
- imageManager->onSpriteLoaded();
- }
-
-
- const LayerDifference layerDiff = diffLayers(layerImpls, parameters.layers);
- layerImpls = parameters.layers;
-
- // Remove render layers for removed layers.
- for (const auto& entry : layerDiff.removed) {
- renderLayers.erase(entry.first);
- }
-
- // Create render layers for newly added layers.
- for (const auto& entry : layerDiff.added) {
- renderLayers.emplace(entry.first, RenderLayer::create(entry.second));
- }
-
- // Update render layers for changed layers.
- for (const auto& entry : layerDiff.changed) {
- renderLayers.at(entry.first)->setImpl(entry.second.after);
- }
-
- // Update layers for class and zoom changes.
- for (const auto& entry : renderLayers) {
- RenderLayer& layer = *entry.second;
- const bool layerAdded = layerDiff.added.count(entry.first);
- const bool layerChanged = layerDiff.changed.count(entry.first);
-
- if (layerAdded || layerChanged) {
- layer.transition(transitionParameters);
- }
-
- if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
- layer.evaluate(evaluationParameters);
- }
- }
-
-
- const SourceDifference sourceDiff = diffSources(sourceImpls, parameters.sources);
- sourceImpls = parameters.sources;
-
- // Remove render layers for removed sources.
- for (const auto& entry : sourceDiff.removed) {
- renderSources.erase(entry.first);
- }
-
- // Create render sources for newly added sources.
- for (const auto& entry : sourceDiff.added) {
- std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
- renderSource->setObserver(this);
- renderSources.emplace(entry.first, std::move(renderSource));
- }
-
- // Update all sources.
- for (const auto& source : *sourceImpls) {
- std::vector<Immutable<Layer::Impl>> filteredLayers;
- bool needsRendering = false;
- bool needsRelayout = false;
-
- for (const auto& layer : *layerImpls) {
- if (layer->type == LayerType::Background ||
- layer->type == LayerType::Custom ||
- layer->source != source->id) {
- continue;
- }
-
- if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) {
- needsRendering = true;
- }
-
- if (!needsRelayout && (
- hasLayoutDifference(layerDiff, layer->id) ||
- !imageDiff.added.empty() ||
- !imageDiff.removed.empty() ||
- !imageDiff.changed.empty())) {
- needsRelayout = true;
- }
-
- filteredLayers.push_back(layer);
- }
-
- renderSources.at(source->id)->update(source,
- filteredLayers,
- needsRendering,
- needsRelayout,
- tileParameters);
- }
-}
-
-RenderSource* RenderStyle::getRenderSource(const std::string& id) const {
- auto it = renderSources.find(id);
- return it != renderSources.end() ? it->second.get() : nullptr;
-}
-
-bool RenderStyle::hasTransitions() const {
- if (renderLight.hasTransition()) {
- return true;
- }
-
- for (const auto& entry : renderLayers) {
- if (entry.second->hasTransition()) {
- return true;
- }
- }
-
- return false;
-}
-
-bool RenderStyle::isLoaded() const {
- for (const auto& entry: renderSources) {
- if (!entry.second->isLoaded()) {
- return false;
- }
- }
-
- if (!imageManager->isLoaded()) {
- return false;
- }
-
- return true;
-}
-
-RenderData RenderStyle::getRenderData(MapDebugOptions debugOptions, float angle) {
- RenderData result;
-
- for (const auto& entry : renderSources) {
- if (entry.second->isEnabled()) {
- result.sources.insert(entry.second.get());
- }
- }
-
- for (auto& layerImpl : *layerImpls) {
- RenderLayer* layer = getRenderLayer(layerImpl->id);
- assert(layer);
-
- if (!layer->needsRendering(zoomHistory.lastZoom)) {
- continue;
- }
-
- if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
- if (debugOptions & MapDebugOptions::Overdraw) {
- // We want to skip glClear optimization in overdraw mode.
- result.order.emplace_back(*layer, nullptr);
- continue;
- }
- const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
- if (layerImpl.get() == layerImpls->at(0).get() && paint.get<BackgroundPattern>().from.empty()) {
- // This is a solid background. We can use glClear().
- result.backgroundColor = paint.get<BackgroundColor>() * paint.get<BackgroundOpacity>();
- } else {
- // This is a textured background, or not the bottommost layer. We need to render it with a quad.
- result.order.emplace_back(*layer, nullptr);
- }
- continue;
- }
-
- if (layer->is<RenderCustomLayer>()) {
- result.order.emplace_back(*layer, nullptr);
- continue;
- }
-
- RenderSource* source = getRenderSource(layer->baseImpl->source);
- if (!source) {
- Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str());
- continue;
- }
-
- auto& renderTiles = source->getRenderTiles();
- const bool symbolLayer = layer->is<RenderSymbolLayer>();
-
- // Sort symbol tiles in opposite y position, so tiles with overlapping
- // symbols are drawn on top of each other, with lower symbols being
- // drawn on top of higher symbols.
- std::vector<std::reference_wrapper<RenderTile>> sortedTiles;
- std::transform(renderTiles.begin(), renderTiles.end(), std::back_inserter(sortedTiles),
- [](auto& pair) { return std::ref(pair.second); });
- if (symbolLayer) {
- std::sort(sortedTiles.begin(), sortedTiles.end(),
- [angle](const RenderTile& a, const RenderTile& b) {
- Point<float> pa(a.id.canonical.x, a.id.canonical.y);
- Point<float> pb(b.id.canonical.x, b.id.canonical.y);
-
- auto par = util::rotate(pa, angle);
- auto pbr = util::rotate(pb, angle);
-
- return std::tie(par.y, par.x) < std::tie(pbr.y, pbr.x);
- });
- }
-
- std::vector<std::reference_wrapper<RenderTile>> sortedTilesForInsertion;
- for (auto& sortedTile : sortedTiles) {
- auto& tile = sortedTile.get();
- if (!tile.tile.isRenderable()) {
- 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;
- }
- }
- layer->setRenderTiles(std::move(sortedTilesForInsertion));
- result.order.emplace_back(*layer, source);
- }
-
- return result;
-}
-
-std::vector<Feature> RenderStyle::queryRenderedFeatures(const ScreenLineString& geometry,
- const TransformState& transformState,
- const RenderedQueryOptions& options) const {
- std::unordered_map<std::string, std::vector<Feature>> resultsByLayer;
-
- if (options.layerIDs) {
- std::unordered_set<std::string> sourceIDs;
- for (const auto& layerID : *options.layerIDs) {
- if (const RenderLayer* layer = getRenderLayer(layerID)) {
- sourceIDs.emplace(layer->baseImpl->source);
- }
- }
- for (const auto& sourceID : sourceIDs) {
- if (RenderSource* renderSource = getRenderSource(sourceID)) {
- auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, *this, options);
- std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
- }
- }
- } else {
- for (const auto& entry : renderSources) {
- auto sourceResults = entry.second->queryRenderedFeatures(geometry, transformState, *this, options);
- std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
- }
- }
-
- std::vector<Feature> result;
-
- if (resultsByLayer.empty()) {
- return result;
- }
-
- // Combine all results based on the style layer order.
- for (const auto& layerImpl : *layerImpls) {
- const RenderLayer* layer = getRenderLayer(layerImpl->id);
- if (!layer->needsRendering(zoomHistory.lastZoom)) {
- continue;
- }
- auto it = resultsByLayer.find(layer->baseImpl->id);
- if (it != resultsByLayer.end()) {
- std::move(it->second.begin(), it->second.end(), std::back_inserter(result));
- }
- }
-
- return result;
-}
-
-void RenderStyle::setSourceTileCacheSize(size_t size) {
- for (const auto& entry : renderSources) {
- entry.second->setCacheSize(size);
- }
-}
-
-void RenderStyle::onLowMemory() {
- for (const auto& entry : renderSources) {
- entry.second->onLowMemory();
- }
-}
-
-void RenderStyle::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
- Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
- glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str());
- observer->onResourceError(error);
-}
-
-void RenderStyle::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
- Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
- util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str());
- observer->onResourceError(error);
-}
-
-void RenderStyle::onTileChanged(RenderSource&, const OverscaledTileID&) {
- observer->onInvalidate();
-}
-
-void RenderStyle::dumpDebugLogs() const {
- for (const auto& entry : renderSources) {
- entry.second->dumpDebugLogs();
- }
-
- imageManager->dumpDebugLogs();
-}
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style.hpp b/src/mbgl/renderer/render_style.hpp
deleted file mode 100644
index dc33e7b2f4..0000000000
--- a/src/mbgl/renderer/render_style.hpp
+++ /dev/null
@@ -1,93 +0,0 @@
-#pragma once
-
-#include <mbgl/style/image.hpp>
-#include <mbgl/renderer/render_source.hpp>
-#include <mbgl/renderer/render_source_observer.hpp>
-#include <mbgl/renderer/render_layer.hpp>
-#include <mbgl/renderer/render_light.hpp>
-#include <mbgl/text/glyph_manager_observer.hpp>
-#include <mbgl/map/zoom_history.hpp>
-#include <mbgl/map/mode.hpp>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace mbgl {
-
-class FileSource;
-class GlyphManager;
-class ImageManager;
-class LineAtlas;
-class RenderData;
-class TransformState;
-class RenderedQueryOptions;
-class Scheduler;
-class UpdateParameters;
-class RenderStyleObserver;
-
-namespace style {
-class Image;
-class Source;
-class Layer;
-} // namespace style
-
-class RenderStyle : public GlyphManagerObserver,
- public RenderSourceObserver {
-public:
- RenderStyle(Scheduler&, FileSource&);
- ~RenderStyle() final;
-
- void setObserver(RenderStyleObserver*);
- void update(const UpdateParameters&);
-
- bool isLoaded() const;
- bool hasTransitions() const;
-
- RenderSource* getRenderSource(const std::string& id) const;
-
- std::vector<const RenderLayer*> getRenderLayers() const;
-
- RenderLayer* getRenderLayer(const std::string& id);
- const RenderLayer* getRenderLayer(const std::string& id) const;
-
- const RenderLight& getRenderLight() const;
-
- RenderData getRenderData(MapDebugOptions, float angle);
-
- std::vector<Feature> queryRenderedFeatures(const ScreenLineString& geometry,
- const TransformState& transformState,
- const RenderedQueryOptions& options) const;
-
- void setSourceTileCacheSize(size_t);
- void onLowMemory();
-
- void dumpDebugLogs() const;
-
- Scheduler& scheduler;
- FileSource& fileSource;
- std::unique_ptr<GlyphManager> glyphManager;
- std::unique_ptr<ImageManager> imageManager;
- std::unique_ptr<LineAtlas> lineAtlas;
-
-private:
- Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls;
- Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls;
- Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls;
-
- std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources;
- std::unordered_map<std::string, std::unique_ptr<RenderLayer>> renderLayers;
- RenderLight renderLight;
-
- // GlyphManagerObserver implementation.
- void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
-
- // RenderSourceObserver implementation.
- void onTileChanged(RenderSource&, const OverscaledTileID&) override;
- void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
-
- RenderStyleObserver* observer;
- ZoomHistory zoomHistory;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_style_observer.hpp b/src/mbgl/renderer/render_style_observer.hpp
deleted file mode 100644
index 5852dd68b8..0000000000
--- a/src/mbgl/renderer/render_style_observer.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <exception>
-
-namespace mbgl {
-
-class RenderStyleObserver {
-public:
- virtual ~RenderStyleObserver() = default;
- virtual void onInvalidate() {}
- virtual void onResourceError(std::exception_ptr) {}
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp
index 59c3ea076b..8df31f8d7c 100644
--- a/src/mbgl/renderer/render_tile.cpp
+++ b/src/mbgl/renderer/render_tile.cpp
@@ -1,7 +1,11 @@
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/buckets/debug_bucket.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/tile/tile.hpp>
+#include <mbgl/util/math.hpp>
namespace mbgl {
@@ -10,24 +14,26 @@ using namespace style;
mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix,
const std::array<float, 2>& translation,
TranslateAnchorType anchor,
- const TransformState& state) const {
+ const TransformState& state,
+ const bool inViewportPixelUnits) const {
if (translation[0] == 0 && translation[1] == 0) {
return tileMatrix;
}
mat4 vtxMatrix;
- if (anchor == TranslateAnchorType::Viewport) {
- const double sin_a = std::sin(-state.getAngle());
- const double cos_a = std::cos(-state.getAngle());
- matrix::translate(vtxMatrix, tileMatrix,
- id.pixelsToTileUnits(translation[0] * cos_a - translation[1] * sin_a, state.getZoom()),
- id.pixelsToTileUnits(translation[0] * sin_a + translation[1] * cos_a, state.getZoom()),
- 0);
+ const float angle = inViewportPixelUnits ?
+ (anchor == TranslateAnchorType::Map ? state.getAngle() : 0) :
+ (anchor == TranslateAnchorType::Viewport ? -state.getAngle() : 0);
+
+ Point<float> translate = util::rotate(Point<float>{ translation[0], translation[1] }, angle);
+
+ if (inViewportPixelUnits) {
+ matrix::translate(vtxMatrix, tileMatrix, translate.x, translate.y, 0);
} else {
matrix::translate(vtxMatrix, tileMatrix,
- id.pixelsToTileUnits(translation[0], state.getZoom()),
- id.pixelsToTileUnits(translation[1], state.getZoom()),
+ id.pixelsToTileUnits(translate.x, state.getZoom()),
+ id.pixelsToTileUnits(translate.y, state.getZoom()),
0);
}
@@ -37,24 +43,107 @@ mat4 RenderTile::translateVtxMatrix(const mat4& tileMatrix,
mat4 RenderTile::translatedMatrix(const std::array<float, 2>& translation,
TranslateAnchorType anchor,
const TransformState& state) const {
- return translateVtxMatrix(matrix, translation, anchor, state);
+ return translateVtxMatrix(matrix, translation, anchor, state, false);
}
mat4 RenderTile::translatedClipMatrix(const std::array<float, 2>& translation,
TranslateAnchorType anchor,
const TransformState& state) const {
- return translateVtxMatrix(nearClippedMatrix, translation, anchor, state);
+ return translateVtxMatrix(nearClippedMatrix, translation, anchor, state, false);
}
-void RenderTile::startRender(Painter& painter) {
- tile.upload(painter.context);
+void RenderTile::setMask(TileMask&& mask) {
+ tile.setMask(std::move(mask));
+}
+
+void RenderTile::startRender(PaintParameters& parameters) {
+ tile.upload(parameters.context);
// Calculate two matrices for this tile: matrix is the standard tile matrix; nearClippedMatrix
// clips the near plane to 100 to save depth buffer precision
- painter.state.matrixFor(matrix, id);
- painter.state.matrixFor(nearClippedMatrix, id);
- matrix::multiply(matrix, painter.projMatrix, matrix);
- matrix::multiply(nearClippedMatrix, painter.nearClippedProjMatrix, nearClippedMatrix);
+ parameters.state.matrixFor(matrix, id);
+ parameters.state.matrixFor(nearClippedMatrix, id);
+ matrix::multiply(matrix, parameters.projMatrix, matrix);
+ matrix::multiply(nearClippedMatrix, parameters.nearClippedProjMatrix, nearClippedMatrix);
+}
+
+void RenderTile::finishRender(PaintParameters& parameters) {
+ if (!used || parameters.debugOptions == MapDebugOptions::NoDebug)
+ return;
+
+ static const style::Properties<>::PossiblyEvaluated properties {};
+ static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+
+ if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) {
+ if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() ||
+ tile.debugBucket->complete != tile.isComplete() ||
+ !(tile.debugBucket->modified == tile.modified) ||
+ !(tile.debugBucket->expires == tile.expires) ||
+ tile.debugBucket->debugMode != parameters.debugOptions) {
+ tile.debugBucket = std::make_unique<DebugBucket>(
+ tile.id, tile.isRenderable(), tile.isComplete(), tile.modified,
+ tile.expires, parameters.debugOptions, parameters.context);
+ }
+
+ parameters.programs.debug.draw(
+ parameters.context,
+ gl::Lines { 4.0f * parameters.pixelRatio },
+ gl::DepthMode::disabled(),
+ parameters.stencilModeForClipping(clip),
+ gl::ColorMode::unblended(),
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::white() }
+ },
+ *tile.debugBucket->vertexBuffer,
+ *tile.debugBucket->indexBuffer,
+ tile.debugBucket->segments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
+
+ parameters.programs.debug.draw(
+ parameters.context,
+ gl::Lines { 2.0f * parameters.pixelRatio },
+ gl::DepthMode::disabled(),
+ parameters.stencilModeForClipping(clip),
+ gl::ColorMode::unblended(),
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::black() }
+ },
+ *tile.debugBucket->vertexBuffer,
+ *tile.debugBucket->indexBuffer,
+ tile.debugBucket->segments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
+ }
+
+ if (parameters.debugOptions & MapDebugOptions::TileBorders) {
+ parameters.programs.debug.draw(
+ parameters.context,
+ gl::LineStrip { 4.0f * parameters.pixelRatio },
+ gl::DepthMode::disabled(),
+ parameters.stencilModeForClipping(clip),
+ gl::ColorMode::unblended(),
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::red() }
+ },
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.tileBorderIndexBuffer,
+ parameters.staticData.tileBorderSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
+ }
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/render_tile.hpp b/src/mbgl/renderer/render_tile.hpp
index 6677278873..3db02393d2 100644
--- a/src/mbgl/renderer/render_tile.hpp
+++ b/src/mbgl/renderer/render_tile.hpp
@@ -4,6 +4,7 @@
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/clip_id.hpp>
#include <mbgl/style/types.hpp>
+#include <mbgl/renderer/tile_mask.hpp>
#include <array>
@@ -11,9 +12,9 @@ namespace mbgl {
class Tile;
class TransformState;
-class Painter;
+class PaintParameters;
-class RenderTile {
+class RenderTile final {
public:
RenderTile(UnwrappedTileID id_, Tile& tile_) : id(std::move(id_)), tile(tile_) {}
RenderTile(const RenderTile&) = delete;
@@ -27,6 +28,7 @@ public:
mat4 matrix;
mat4 nearClippedMatrix;
bool used = false;
+ bool needsClipping = false;
mat4 translatedMatrix(const std::array<float, 2>& translate,
style::TranslateAnchorType anchor,
@@ -36,13 +38,15 @@ public:
style::TranslateAnchorType anchor,
const TransformState&) const;
- void startRender(Painter&);
+ void setMask(TileMask&&);
+ void startRender(PaintParameters&);
+ void finishRender(PaintParameters&);
-private:
mat4 translateVtxMatrix(const mat4& tileMatrix,
const std::array<float, 2>& translation,
style::TranslateAnchorType anchor,
- const TransformState& state) const;
+ const TransformState& state,
+ const bool inViewportPixelUnits) const;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp
new file mode 100644
index 0000000000..6d086c70b1
--- /dev/null
+++ b/src/mbgl/renderer/renderer.cpp
@@ -0,0 +1,102 @@
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/renderer/renderer_impl.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/annotation/annotation_manager.hpp>
+
+namespace mbgl {
+
+Renderer::Renderer(RendererBackend& backend,
+ float pixelRatio_,
+ FileSource& fileSource_,
+ Scheduler& scheduler_,
+ GLContextMode contextMode_,
+ const optional<std::string> programCacheDir_,
+ const optional<std::string> localFontFamily_)
+ : impl(std::make_unique<Impl>(backend, pixelRatio_, fileSource_, scheduler_,
+ contextMode_, std::move(programCacheDir_), std::move(localFontFamily_))) {
+}
+
+Renderer::~Renderer() {
+ BackendScope guard { impl->backend };
+ impl.reset();
+}
+
+void Renderer::markContextLost() {
+ impl->markContextLost();
+}
+
+void Renderer::setObserver(RendererObserver* observer) {
+ impl->setObserver(observer);
+}
+
+void Renderer::render(const UpdateParameters& updateParameters) {
+ impl->render(updateParameters);
+}
+
+std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const {
+ return impl->queryRenderedFeatures(geometry, options);
+}
+
+std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options) const {
+ return impl->queryRenderedFeatures({ point }, options);
+}
+
+std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options) const {
+ return impl->queryRenderedFeatures(
+ {
+ box.min,
+ {box.max.x, box.min.y},
+ box.max,
+ {box.min.x, box.max.y},
+ box.min
+ },
+ options
+ );
+}
+
+AnnotationIDs Renderer::queryPointAnnotations(const ScreenBox& box) const {
+ RenderedQueryOptions options;
+ options.layerIDs = {{ AnnotationManager::PointLayerID }};
+ auto features = queryRenderedFeatures(box, options);
+ return getAnnotationIDs(features);
+}
+
+AnnotationIDs Renderer::queryShapeAnnotations(const ScreenBox& box) const {
+ auto features = impl->queryShapeAnnotations({
+ box.min,
+ {box.max.x, box.min.y},
+ box.max,
+ {box.min.x, box.max.y},
+ box.min
+ });
+ return getAnnotationIDs(features);
+}
+
+AnnotationIDs Renderer::getAnnotationIDs(const std::vector<Feature>& features) const {
+ std::set<AnnotationID> set;
+ for (auto &feature : features) {
+ assert(feature.id);
+ assert(feature.id->is<uint64_t>());
+ assert(feature.id->get<uint64_t>() <= std::numeric_limits<AnnotationID>::max());
+ set.insert(static_cast<AnnotationID>(feature.id->get<uint64_t>()));
+ }
+ AnnotationIDs ids;
+ ids.reserve(set.size());
+ std::move(set.begin(), set.end(), std::back_inserter(ids));
+ return ids;
+}
+
+std::vector<Feature> Renderer::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const {
+ return impl->querySourceFeatures(sourceID, options);
+}
+
+void Renderer::dumpDebugLogs() {
+ impl->dumDebugLogs();
+}
+
+void Renderer::onLowMemory() {
+ BackendScope guard { impl->backend };
+ impl->onLowMemory();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/map/backend.cpp b/src/mbgl/renderer/renderer_backend.cpp
index 83c2fed00b..22d263313c 100644
--- a/src/mbgl/map/backend.cpp
+++ b/src/mbgl/renderer/renderer_backend.cpp
@@ -1,5 +1,5 @@
-#include <mbgl/map/backend.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/extension.hpp>
#include <mbgl/gl/debugging.hpp>
@@ -8,62 +8,62 @@
namespace mbgl {
-Backend::Backend() = default;
+RendererBackend::RendererBackend() = default;
-gl::Context& Backend::getContext() {
+gl::Context& RendererBackend::getContext() {
assert(BackendScope::exists());
std::call_once(initialized, [this] {
context = std::make_unique<gl::Context>();
context->enableDebugging();
context->initializeExtensions(
- std::bind(&Backend::initializeExtension, this, std::placeholders::_1));
+ std::bind(&RendererBackend::getExtensionFunctionPointer, this, std::placeholders::_1));
});
return *context;
}
-PremultipliedImage Backend::readFramebuffer(const Size& size) const {
+PremultipliedImage RendererBackend::readFramebuffer(const Size& size) const {
assert(context);
return context->readFramebuffer<PremultipliedImage>(size);
}
-void Backend::assumeFramebufferBinding(const gl::FramebufferID fbo) {
+void RendererBackend::assumeFramebufferBinding(const gl::FramebufferID fbo) {
getContext().bindFramebuffer.setCurrentValue(fbo);
if (fbo != ImplicitFramebufferBinding) {
assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue());
}
}
-void Backend::assumeViewport(int32_t x, int32_t y, const Size& size) {
+void RendererBackend::assumeViewport(int32_t x, int32_t y, const Size& size) {
getContext().viewport.setCurrentValue({ x, y, size });
assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue());
}
-void Backend::assumeScissorTest(bool enabled) {
+void RendererBackend::assumeScissorTest(bool enabled) {
getContext().scissorTest.setCurrentValue(enabled);
assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue());
}
-bool Backend::implicitFramebufferBound() {
+bool RendererBackend::implicitFramebufferBound() {
return getContext().bindFramebuffer.getCurrentValue() == ImplicitFramebufferBinding;
}
-void Backend::setFramebufferBinding(const gl::FramebufferID fbo) {
+void RendererBackend::setFramebufferBinding(const gl::FramebufferID fbo) {
getContext().bindFramebuffer = fbo;
if (fbo != ImplicitFramebufferBinding) {
assert(gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue());
}
}
-void Backend::setViewport(int32_t x, int32_t y, const Size& size) {
+void RendererBackend::setViewport(int32_t x, int32_t y, const Size& size) {
getContext().viewport = { x, y, size };
assert(gl::value::Viewport::Get() == getContext().viewport.getCurrentValue());
}
-void Backend::setScissorTest(bool enabled) {
+void RendererBackend::setScissorTest(bool enabled) {
getContext().scissorTest = enabled;
assert(gl::value::ScissorTest::Get() == getContext().scissorTest.getCurrentValue());
}
-Backend::~Backend() = default;
+RendererBackend::~RendererBackend() = default;
} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp
new file mode 100644
index 0000000000..4bed0e251b
--- /dev/null
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -0,0 +1,815 @@
+#include <mbgl/annotation/annotation_manager.hpp>
+#include <mbgl/renderer/renderer_impl.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/renderer_observer.hpp>
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/renderer/update_parameters.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/transition_parameters.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#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/style_diff.hpp>
+#include <mbgl/renderer/query.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/gl/debugging.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/style/source_impl.hpp>
+#include <mbgl/style/transition_options.hpp>
+#include <mbgl/text/glyph_manager.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/logging.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+static RendererObserver& nullObserver() {
+ static RendererObserver observer;
+ return observer;
+}
+
+Renderer::Impl::Impl(RendererBackend& backend_,
+ float pixelRatio_,
+ FileSource& fileSource_,
+ Scheduler& scheduler_,
+ GLContextMode contextMode_,
+ const optional<std::string> programCacheDir_,
+ const optional<std::string> localFontFamily_)
+ : backend(backend_)
+ , scheduler(scheduler_)
+ , fileSource(fileSource_)
+ , observer(&nullObserver())
+ , contextMode(contextMode_)
+ , pixelRatio(pixelRatio_)
+ , programCacheDir(programCacheDir_)
+ , glyphManager(std::make_unique<GlyphManager>(fileSource, std::make_unique<LocalGlyphRasterizer>(localFontFamily_)))
+ , imageManager(std::make_unique<ImageManager>())
+ , lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 }))
+ , 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>())
+ , placement(std::make_unique<Placement>(TransformState{}, MapMode::Static)) {
+ glyphManager->setObserver(this);
+}
+
+Renderer::Impl::~Impl() {
+ assert(BackendScope::exists());
+
+ if (contextLost) {
+ // Signal all RenderCustomLayers that the context was lost
+ // before cleaning up
+ for (const auto& entry : renderLayers) {
+ RenderLayer& layer = *entry.second;
+ if (layer.is<RenderCustomLayer>()) {
+ layer.as<RenderCustomLayer>()->markContextDestroyed();
+ }
+ }
+ }
+};
+
+void Renderer::Impl::setObserver(RendererObserver* observer_) {
+ observer = observer_ ? observer_ : &nullObserver();
+}
+
+void Renderer::Impl::render(const UpdateParameters& updateParameters) {
+ if (updateParameters.mode != MapMode::Continuous) {
+ // Don't load/render anyting in still mode until explicitly requested.
+ if (!updateParameters.stillImageRequest) {
+ return;
+ }
+
+ // Reset zoom history state.
+ zoomHistory.first = true;
+ }
+
+ assert(BackendScope::exists());
+
+ updateParameters.annotationManager.updateData();
+
+ const bool zoomChanged = zoomHistory.update(updateParameters.transformState.getZoom(), updateParameters.timePoint);
+
+ const TransitionParameters transitionParameters {
+ updateParameters.timePoint,
+ updateParameters.mode == MapMode::Continuous ? updateParameters.transitionOptions : TransitionOptions()
+ };
+
+ const PropertyEvaluationParameters evaluationParameters {
+ zoomHistory,
+ updateParameters.timePoint,
+ updateParameters.mode == MapMode::Continuous ? util::DEFAULT_TRANSITION_DURATION : Duration::zero()
+ };
+
+ const TileParameters tileParameters {
+ updateParameters.pixelRatio,
+ updateParameters.debugOptions,
+ updateParameters.transformState,
+ scheduler,
+ fileSource,
+ updateParameters.mode,
+ updateParameters.annotationManager,
+ *imageManager,
+ *glyphManager,
+ updateParameters.prefetchZoomDelta
+ };
+
+ glyphManager->setURL(updateParameters.glyphURL);
+
+ // Update light.
+ const bool lightChanged = renderLight.impl != updateParameters.light;
+
+ if (lightChanged) {
+ renderLight.impl = updateParameters.light;
+ renderLight.transition(transitionParameters);
+ }
+
+ if (lightChanged || zoomChanged || renderLight.hasTransition()) {
+ renderLight.evaluate(evaluationParameters);
+ }
+
+
+ const ImageDifference imageDiff = diffImages(imageImpls, updateParameters.images);
+ imageImpls = updateParameters.images;
+
+ // Remove removed images from sprite atlas.
+ for (const auto& entry : imageDiff.removed) {
+ imageManager->removeImage(entry.first);
+ }
+
+ // Add added images to sprite atlas.
+ for (const auto& entry : imageDiff.added) {
+ imageManager->addImage(entry.second);
+ }
+
+ // Update changed images.
+ for (const auto& entry : imageDiff.changed) {
+ imageManager->updateImage(entry.second.after);
+ }
+
+ imageManager->setLoaded(updateParameters.spriteLoaded);
+
+
+ const LayerDifference layerDiff = diffLayers(layerImpls, updateParameters.layers);
+ layerImpls = updateParameters.layers;
+
+ // Remove render layers for removed layers.
+ for (const auto& entry : layerDiff.removed) {
+ renderLayers.erase(entry.first);
+ }
+
+ // Create render layers for newly added layers.
+ for (const auto& entry : layerDiff.added) {
+ renderLayers.emplace(entry.first, RenderLayer::create(entry.second));
+ }
+
+ // Update render layers for changed layers.
+ for (const auto& entry : layerDiff.changed) {
+ renderLayers.at(entry.first)->setImpl(entry.second.after);
+ }
+
+ // Update layers for class and zoom changes.
+ for (const auto& entry : renderLayers) {
+ RenderLayer& layer = *entry.second;
+ const bool layerAdded = layerDiff.added.count(entry.first);
+ const bool layerChanged = layerDiff.changed.count(entry.first);
+
+ if (layerAdded || layerChanged) {
+ layer.transition(transitionParameters);
+ }
+
+ if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) {
+ layer.evaluate(evaluationParameters);
+ }
+ }
+
+
+ const SourceDifference sourceDiff = diffSources(sourceImpls, updateParameters.sources);
+ sourceImpls = updateParameters.sources;
+
+ // Remove render layers for removed sources.
+ for (const auto& entry : sourceDiff.removed) {
+ renderSources.erase(entry.first);
+ }
+
+ // Create render sources for newly added sources.
+ for (const auto& entry : sourceDiff.added) {
+ std::unique_ptr<RenderSource> renderSource = RenderSource::create(entry.second);
+ renderSource->setObserver(this);
+ renderSources.emplace(entry.first, std::move(renderSource));
+ }
+
+ const bool hasImageDiff = !(imageDiff.added.empty() && imageDiff.removed.empty() && imageDiff.changed.empty());
+
+ // Update all sources.
+ for (const auto& source : *sourceImpls) {
+ std::vector<Immutable<Layer::Impl>> filteredLayers;
+ bool needsRendering = false;
+ bool needsRelayout = false;
+
+ for (const auto& layer : *layerImpls) {
+ if (layer->type == LayerType::Background ||
+ layer->type == LayerType::Custom ||
+ layer->source != source->id) {
+ continue;
+ }
+
+ if (!needsRendering && getRenderLayer(layer->id)->needsRendering(zoomHistory.lastZoom)) {
+ needsRendering = true;
+ }
+
+ if (!needsRelayout && (hasImageDiff || hasLayoutDifference(layerDiff, layer->id))) {
+ needsRelayout = true;
+ }
+
+ filteredLayers.push_back(layer);
+ }
+
+ renderSources.at(source->id)->update(source,
+ filteredLayers,
+ needsRendering,
+ needsRelayout,
+ tileParameters);
+ }
+
+ transformState = updateParameters.transformState;
+
+ if (!staticData) {
+ staticData = std::make_unique<RenderStaticData>(backend.getContext(), pixelRatio, programCacheDir);
+ }
+
+ PaintParameters parameters {
+ backend.getContext(),
+ pixelRatio,
+ contextMode,
+ backend,
+ updateParameters,
+ renderLight.getEvaluated(),
+ *staticData,
+ *imageManager,
+ *lineAtlas
+ };
+
+ bool loaded = updateParameters.styleLoaded && isLoaded();
+ if (updateParameters.mode != MapMode::Continuous && !loaded) {
+ return;
+ }
+
+ if (renderState == RenderState::Never) {
+ observer->onWillStartRenderingMap();
+ }
+
+ observer->onWillStartRenderingFrame();
+
+ backend.updateAssumedState();
+
+ if (parameters.contextMode == GLContextMode::Shared) {
+ parameters.context.setDirtyState();
+ }
+
+ Color backgroundColor;
+
+ class RenderItem {
+ public:
+ RenderLayer& layer;
+ RenderSource* source;
+ };
+
+ std::vector<RenderItem> order;
+
+ for (auto& layerImpl : *layerImpls) {
+ RenderLayer* layer = getRenderLayer(layerImpl->id);
+ assert(layer);
+
+ if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) {
+ parameters.staticData.has3D = true;
+ }
+
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+
+ if (const RenderBackgroundLayer* background = layer->as<RenderBackgroundLayer>()) {
+ const BackgroundPaintProperties::PossiblyEvaluated& paint = background->evaluated;
+ 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 {
+ // This is a textured background, or not the bottommost layer. We need to render it with a quad.
+ order.emplace_back(RenderItem { *layer, nullptr });
+ }
+ continue;
+ }
+
+ if (layer->is<RenderCustomLayer>()) {
+ order.emplace_back(RenderItem { *layer, nullptr });
+ continue;
+ }
+
+ RenderSource* source = getRenderSource(layer->baseImpl->source);
+ if (!source) {
+ Log::Warning(Event::Render, "can't find source for layer '%s'", layer->getID().c_str());
+ continue;
+ }
+
+ const bool symbolLayer = layer->is<RenderSymbolLayer>();
+
+ auto sortedTiles = source->getRenderTiles();
+ if (symbolLayer) {
+ // Sort symbol tiles in opposite y position, so tiles with overlapping symbols are drawn
+ // on top of each other, with lower symbols being drawn on top of higher symbols.
+ std::sort(sortedTiles.begin(), sortedTiles.end(), [&](const RenderTile& a, const RenderTile& b) {
+ Point<float> pa(a.id.canonical.x, a.id.canonical.y);
+ Point<float> pb(b.id.canonical.x, b.id.canonical.y);
+
+ auto par = util::rotate(pa, parameters.state.getAngle());
+ auto pbr = util::rotate(pb, parameters.state.getAngle());
+
+ 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;
+ for (auto& sortedTile : sortedTiles) {
+ auto& tile = sortedTile.get();
+ if (!tile.tile.isRenderable()) {
+ 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.
+ if (!symbolLayer) {
+ tile.needsClipping = true;
+ }
+ }
+ }
+ layer->setRenderTiles(std::move(sortedTilesForInsertion));
+ order.emplace_back(RenderItem { *layer, source });
+ }
+
+ 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);
+ for (auto it = order.rbegin(); it != order.rend(); ++it) {
+ if (it->layer.is<RenderSymbolLayer>()) {
+ newPlacement->placeLayer(*it->layer.as<RenderSymbolLayer>(), parameters.projMatrix, parameters.debugOptions & MapDebugOptions::Collision);
+ }
+ }
+
+ placementChanged = newPlacement->commit(*placement, parameters.timePoint);
+ 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.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "upload");
+
+ parameters.imageManager.upload(parameters.context, 0);
+ parameters.lineAtlas.upload(parameters.context, 0);
+
+ // Update all clipping IDs + upload buckets.
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ entry.second->startRender(parameters);
+ }
+ }
+ }
+
+ // - 3D PASS -------------------------------------------------------------------------------------
+ // Renders any 3D layers bottom-to-top to unique FBOs with texture attachments, but share the same
+ // depth rbo between them.
+ if (parameters.staticData.has3D) {
+ parameters.staticData.backendSize = parameters.backend.getFramebufferSize();
+
+ MBGL_DEBUG_GROUP(parameters.context, "3d");
+ parameters.pass = RenderPass::Pass3D;
+
+ if (!parameters.staticData.depthRenderbuffer ||
+ parameters.staticData.depthRenderbuffer->size != parameters.staticData.backendSize) {
+ parameters.staticData.depthRenderbuffer =
+ parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(parameters.staticData.backendSize);
+ }
+ parameters.staticData.depthRenderbuffer->shouldClear(true);
+
+ uint32_t i = static_cast<uint32_t>(order.size()) - 1;
+ for (auto it = order.begin(); it != order.end(); ++it, --i) {
+ parameters.currentLayer = i;
+ if (it->layer.hasRenderPass(parameters.pass)) {
+ MBGL_DEBUG_GROUP(parameters.context, it->layer.getID());
+ it->layer.render(parameters, it->source);
+ }
+ }
+ }
+
+ // - CLEAR -------------------------------------------------------------------------------------
+ // 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();
+ 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 ----------------------------------------------------------------------------
+ // Draws the clipping masks to the stencil buffer.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "clipping masks");
+
+ static const style::FillPaintProperties::PossiblyEvaluated properties {};
+ static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+
+ for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) {
+ parameters.staticData.programs.fill.get(properties).draw(
+ parameters.context,
+ gl::Triangles(),
+ gl::DepthMode::disabled(),
+ gl::StencilMode {
+ gl::StencilMode::Always(),
+ static_cast<int32_t>(clipID.second.reference.to_ulong()),
+ 0b11111111,
+ gl::StencilMode::Keep,
+ gl::StencilMode::Keep,
+ gl::StencilMode::Replace
+ },
+ gl::ColorMode::disabled(),
+ FillProgram::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,
+ properties,
+ parameters.state.getZoom(),
+ "clipping"
+ );
+ }
+ }
+
+#if not MBGL_USE_GLES2 and not defined(NDEBUG)
+ // Render tile clip boundaries, using stencil buffer to calculate fill color.
+ if (parameters.debugOptions & MapDebugOptions::StencilClip) {
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.setDepthMode(gl::DepthMode::disabled());
+ parameters.context.setColorMode(gl::ColorMode::unblended());
+ parameters.context.program = 0;
+
+ // Reset the value in case someone else changed it, or it's dirty.
+ parameters.context.pixelTransferStencil = gl::value::PixelTransferStencil::Default;
+
+ // Read the stencil buffer
+ const auto viewport = parameters.context.viewport.getCurrentValue();
+ auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false);
+
+ // Scale the Stencil buffer to cover the entire color space.
+ auto it = image.data.get();
+ auto end = it + viewport.size.width * viewport.size.height;
+ const auto factor = 255.0f / *std::max_element(it, end);
+ for (; it != end; ++it) {
+ *it *= factor;
+ }
+
+ parameters.context.pixelZoom = { 1, 1 };
+ parameters.context.rasterPos = { -1, -1, 0, 1 };
+ parameters.context.drawPixels(image);
+
+ return;
+ }
+#endif
+
+ // Actually render the layers
+
+ parameters.depthRangeSize = 1 - (order.size() + 2) * parameters.numSublayers * parameters.depthEpsilon;
+
+ // - OPAQUE PASS -------------------------------------------------------------------------------
+ // Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
+ {
+ parameters.pass = RenderPass::Opaque;
+ MBGL_DEBUG_GROUP(parameters.context, "opaque");
+
+ uint32_t i = 0;
+ for (auto it = order.rbegin(); it != order.rend(); ++it, ++i) {
+ parameters.currentLayer = i;
+ if (it->layer.hasRenderPass(parameters.pass)) {
+ MBGL_DEBUG_GROUP(parameters.context, it->layer.getID());
+ it->layer.render(parameters, it->source);
+ }
+ }
+ }
+
+ // - TRANSLUCENT PASS --------------------------------------------------------------------------
+ // Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
+ {
+ parameters.pass = RenderPass::Translucent;
+ MBGL_DEBUG_GROUP(parameters.context, "translucent");
+
+ uint32_t i = static_cast<uint32_t>(order.size()) - 1;
+ for (auto it = order.begin(); it != order.end(); ++it, --i) {
+ parameters.currentLayer = i;
+ if (it->layer.hasRenderPass(parameters.pass)) {
+ MBGL_DEBUG_GROUP(parameters.context, it->layer.getID());
+ it->layer.render(parameters, it->source);
+ }
+ }
+ }
+
+ // - DEBUG PASS --------------------------------------------------------------------------------
+ // Renders debug overlays.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "debug");
+
+ // Finalize the rendering, e.g. by calling debug render calls per tile.
+ // This guarantees that we have at least one function per tile called.
+ // When only rendering layers via the stylesheet, it's possible that we don't
+ // ever visit a tile during rendering.
+ for (const auto& entry : renderSources) {
+ if (entry.second->isEnabled()) {
+ entry.second->finishRender(parameters);
+ }
+ }
+ }
+
+#if not MBGL_USE_GLES2 and not defined(NDEBUG)
+ // Render the depth buffer.
+ if (parameters.debugOptions & MapDebugOptions::DepthBuffer) {
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.setDepthMode(gl::DepthMode::disabled());
+ parameters.context.setColorMode(gl::ColorMode::unblended());
+ parameters.context.program = 0;
+
+ // Scales the values in the depth buffer so that they cover the entire grayscale range. This
+ // makes it easier to spot tiny differences.
+ const float base = 1.0f / (1.0f - parameters.depthRangeSize);
+ parameters.context.pixelTransferDepth = { base, 1.0f - base };
+
+ // Read the stencil buffer
+ auto viewport = parameters.context.viewport.getCurrentValue();
+ auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Depth>(viewport.size, false);
+
+ parameters.context.pixelZoom = { 1, 1 };
+ parameters.context.rasterPos = { -1, -1, 0, 1 };
+ parameters.context.drawPixels(image);
+ }
+#endif
+
+ // TODO: Find a better way to unbind VAOs after we're done with them without introducing
+ // unnecessary bind(0)/bind(N) sequences.
+ {
+ MBGL_DEBUG_GROUP(parameters.context, "cleanup");
+
+ parameters.context.activeTextureUnit = 1;
+ parameters.context.texture[1] = 0;
+ parameters.context.activeTextureUnit = 0;
+ parameters.context.texture[0] = 0;
+
+ parameters.context.bindVertexArray = 0;
+ }
+
+ observer->onDidFinishRenderingFrame(
+ loaded ? RendererObserver::RenderMode::Full : RendererObserver::RenderMode::Partial,
+ updateParameters.mode == MapMode::Continuous && hasTransitions(parameters.timePoint)
+ );
+
+ if (!loaded) {
+ renderState = RenderState::Partial;
+ } else if (renderState != RenderState::Fully) {
+ renderState = RenderState::Fully;
+ observer->onDidFinishRenderingMap();
+ }
+
+ // Cleanup only after signaling completion
+ parameters.context.performCleanup();
+}
+
+std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options) const {
+ std::vector<const RenderLayer*> layers;
+ if (options.layerIDs) {
+ for (const auto& layerID : *options.layerIDs) {
+ if (const RenderLayer* layer = getRenderLayer(layerID)) {
+ layers.emplace_back(layer);
+ }
+ }
+ } else {
+ for (const auto& entry : renderLayers) {
+ layers.emplace_back(entry.second.get());
+ }
+ }
+
+ return queryRenderedFeatures(geometry, options, layers);
+}
+
+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;
+ for (const RenderLayer* layer : layers) {
+ sourceIDs.emplace(layer->baseImpl->source);
+ }
+
+ 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, placement->getCollisionIndex());
+ std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
+ }
+ }
+
+ std::vector<Feature> result;
+
+ if (resultsByLayer.empty()) {
+ return result;
+ }
+
+ // Combine all results based on the style layer order.
+ for (const auto& layerImpl : *layerImpls) {
+ const RenderLayer* layer = getRenderLayer(layerImpl->id);
+ if (!layer->needsRendering(zoomHistory.lastZoom)) {
+ continue;
+ }
+ auto it = resultsByLayer.find(layer->baseImpl->id);
+ if (it != resultsByLayer.end()) {
+ std::move(it->second.begin(), it->second.end(), std::back_inserter(result));
+ }
+ }
+
+ return result;
+}
+
+std::vector<Feature> Renderer::Impl::queryShapeAnnotations(const ScreenLineString& geometry) const {
+ std::vector<const RenderLayer*> shapeAnnotationLayers;
+ RenderedQueryOptions options;
+ for (const auto& layerImpl : *layerImpls) {
+ if (std::mismatch(layerImpl->id.begin(), layerImpl->id.end(),
+ AnnotationManager::ShapeLayerID.begin(), AnnotationManager::ShapeLayerID.end()).second == AnnotationManager::ShapeLayerID.end()) {
+ if (const RenderLayer* layer = getRenderLayer(layerImpl->id)) {
+ shapeAnnotationLayers.emplace_back(layer);
+ }
+ }
+ }
+
+ return queryRenderedFeatures(geometry, options, shapeAnnotationLayers);
+}
+
+std::vector<Feature> Renderer::Impl::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const {
+ const RenderSource* source = getRenderSource(sourceID);
+ if (!source) return {};
+
+ return source->querySourceFeatures(options);
+}
+
+void Renderer::Impl::onLowMemory() {
+ assert(BackendScope::exists());
+ backend.getContext().performCleanup();
+ for (const auto& entry : renderSources) {
+ entry.second->onLowMemory();
+ }
+ observer->onInvalidate();
+}
+
+void Renderer::Impl::dumDebugLogs() {
+ for (const auto& entry : renderSources) {
+ entry.second->dumpDebugLogs();
+ }
+
+ imageManager->dumpDebugLogs();
+}
+
+RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+const RenderLayer* Renderer::Impl::getRenderLayer(const std::string& id) const {
+ auto it = renderLayers.find(id);
+ return it != renderLayers.end() ? it->second.get() : nullptr;
+}
+
+RenderSource* Renderer::Impl::getRenderSource(const std::string& id) const {
+ auto it = renderSources.find(id);
+ return it != renderSources.end() ? it->second.get() : nullptr;
+}
+
+bool Renderer::Impl::hasTransitions(TimePoint timePoint) const {
+ if (renderLight.hasTransition()) {
+ return true;
+ }
+
+ for (const auto& entry : renderLayers) {
+ if (entry.second->hasTransition()) {
+ return true;
+ }
+ }
+
+ 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()) {
+ return false;
+ }
+ }
+
+ if (!imageManager->isLoaded()) {
+ return false;
+ }
+
+ return true;
+}
+
+void Renderer::Impl::onGlyphsError(const FontStack& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
+ glyphRange.first, glyphRange.second, fontStackToString(fontStack).c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void Renderer::Impl::onTileError(RenderSource& source, const OverscaledTileID& tileID, std::exception_ptr error) {
+ Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
+ util::toString(tileID).c_str(), source.baseImpl->id.c_str(), util::toString(error).c_str());
+ observer->onResourceError(error);
+}
+
+void Renderer::Impl::onTileChanged(RenderSource&, const OverscaledTileID&) {
+ observer->onInvalidate();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp
new file mode 100644
index 0000000000..5d0200a5df
--- /dev/null
+++ b/src/mbgl/renderer/renderer_impl.hpp
@@ -0,0 +1,121 @@
+#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/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/text/cross_tile_symbol_index.hpp>
+#include <mbgl/text/glyph_manager_observer.hpp>
+#include <mbgl/text/placement.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+class RendererBackend;
+class RendererObserver;
+class RenderSource;
+class RenderLayer;
+class UpdateParameters;
+class RenderStaticData;
+class RenderedQueryOptions;
+class SourceQueryOptions;
+class FileSource;
+class Scheduler;
+class GlyphManager;
+class ImageManager;
+class LineAtlas;
+class CrossTileSymbolIndex;
+
+class Renderer::Impl : public GlyphManagerObserver,
+ public RenderSourceObserver{
+public:
+ Impl(RendererBackend&, float pixelRatio_, FileSource&, Scheduler&, GLContextMode,
+ const optional<std::string> programCacheDir, const optional<std::string> localFontFamily);
+ ~Impl() final;
+
+ void markContextLost() {
+ contextLost = true;
+ };
+
+ void setObserver(RendererObserver*);
+
+ void render(const UpdateParameters&);
+
+ std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&) const;
+ std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const;
+ std::vector<Feature> queryShapeAnnotations(const ScreenLineString&) const;
+
+ void onLowMemory();
+ void dumDebugLogs();
+
+private:
+ bool isLoaded() 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;
+
+ std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector<const RenderLayer*>&) const;
+
+ // GlyphManagerObserver implementation.
+ void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override;
+
+ // RenderSourceObserver implementation.
+ void onTileChanged(RenderSource&, const OverscaledTileID&) override;
+ void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
+
+ void updateFadingTiles();
+
+ friend class Renderer;
+
+ RendererBackend& backend;
+ Scheduler& scheduler;
+ FileSource& fileSource;
+
+ RendererObserver* observer;
+
+ const GLContextMode contextMode;
+ const float pixelRatio;
+ const optional<std::string> programCacheDir;
+
+ enum class RenderState {
+ Never,
+ Partial,
+ Fully,
+ };
+
+ RenderState renderState = RenderState::Never;
+ ZoomHistory zoomHistory;
+ TransformState transformState;
+
+ std::unique_ptr<GlyphManager> glyphManager;
+ std::unique_ptr<ImageManager> imageManager;
+ std::unique_ptr<LineAtlas> lineAtlas;
+ std::unique_ptr<RenderStaticData> staticData;
+
+ Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls;
+ Immutable<std::vector<Immutable<style::Source::Impl>>> sourceImpls;
+ Immutable<std::vector<Immutable<style::Layer::Impl>>> layerImpls;
+
+ std::unordered_map<std::string, std::unique_ptr<RenderSource>> renderSources;
+ 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/renderer_observer.hpp b/src/mbgl/renderer/renderer_observer.hpp
new file mode 100644
index 0000000000..551b5c803e
--- /dev/null
+++ b/src/mbgl/renderer/renderer_observer.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <exception>
+
+namespace mbgl {
+
+class RendererObserver {
+public:
+ virtual ~RendererObserver() = default;
+
+ enum class RenderMode : uint32_t {
+ Partial,
+ Full
+ };
+
+ // Signals that a repaint is required
+ virtual void onInvalidate() {}
+
+ // Resource failed to download / parse
+ virtual void onResourceError(std::exception_ptr) {}
+
+ // First frame
+ virtual void onWillStartRenderingMap() {}
+
+ // Start of frame, initial is the first frame for this map
+ virtual void onWillStartRenderingFrame() {}
+
+ // End of frame, boolean flags that a repaint is required
+ virtual void onDidFinishRenderingFrame(RenderMode, bool) {}
+
+ // Final frame
+ virtual void onDidFinishRenderingMap() {}
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
new file mode 100644
index 0000000000..111f0234ed
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp
@@ -0,0 +1,86 @@
+#include <mbgl/renderer/sources/render_custom_geometry_source.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/tile/custom_geometry_tile.hpp>
+
+#include <mbgl/algorithm/generate_clip_ids.hpp>
+#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderCustomGeometrySource::RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl> impl_)
+ : RenderSource(impl_) {
+ tilePyramid.setObserver(this);
+}
+
+const style::CustomGeometrySource::Impl& RenderCustomGeometrySource::impl() const {
+ return static_cast<const style::CustomGeometrySource::Impl&>(*baseImpl);
+}
+
+bool RenderCustomGeometrySource::isLoaded() const {
+ return tilePyramid.isLoaded();
+}
+
+void RenderCustomGeometrySource::update(Immutable<style::Source::Impl> baseImpl_,
+ const std::vector<Immutable<Layer::Impl>>& layers,
+ const bool needsRendering,
+ const bool needsRelayout,
+ const TileParameters& parameters) {
+ std::swap(baseImpl, baseImpl_);
+
+ enabled = needsRendering;
+
+ auto tileLoader = impl().getTileLoader();
+ if (!tileLoader) {
+ return;
+ }
+
+ tilePyramid.update(layers,
+ needsRendering,
+ needsRelayout,
+ parameters,
+ SourceType::CustomVector,
+ util::tileSize,
+ impl().getZoomRange(),
+ [&] (const OverscaledTileID& tileID) {
+ return std::make_unique<CustomGeometryTile>(tileID, impl().id, parameters, impl().getTileOptions(), *tileLoader);
+ });
+}
+
+void RenderCustomGeometrySource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
+}
+
+void RenderCustomGeometrySource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
+}
+
+std::vector<std::reference_wrapper<RenderTile>> RenderCustomGeometrySource::getRenderTiles() {
+ return tilePyramid.getRenderTiles();
+}
+
+std::unordered_map<std::string, std::vector<Feature>>
+RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
+}
+
+std::vector<Feature> RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const {
+ return tilePyramid.querySourceFeatures(options);
+}
+
+void RenderCustomGeometrySource::onLowMemory() {
+ tilePyramid.onLowMemory();
+}
+
+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..82e691d5c9
--- /dev/null
+++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <mbgl/renderer/render_source.hpp>
+#include <mbgl/renderer/tile_pyramid.hpp>
+#include <mbgl/style/sources/custom_geometry_source_impl.hpp>
+
+namespace mbgl {
+
+class RenderCustomGeometrySource : public RenderSource {
+public:
+ RenderCustomGeometrySource(Immutable<style::CustomGeometrySource::Impl>);
+
+ bool isLoaded() const final;
+
+ void update(Immutable<style::Source::Impl>,
+ const std::vector<Immutable<style::Layer::Impl>>&,
+ bool needsRendering,
+ bool needsRelayout,
+ const TileParameters&) final;
+
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
+
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final;
+
+ std::unordered_map<std::string, std::vector<Feature>>
+ queryRenderedFeatures(const ScreenLineString& geometry,
+ const TransformState& transformState,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
+
+ std::vector<Feature>
+ querySourceFeatures(const SourceQueryOptions&) const final;
+
+ void onLowMemory() 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 337b7b8b7a..d07cfcdc41 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.cpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.cpp
@@ -1,7 +1,8 @@
#include <mbgl/renderer/sources/render_geojson_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/tile/geojson_tile.hpp>
+#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
#include <mbgl/algorithm/generate_clip_ids_impl.hpp>
@@ -34,19 +35,26 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_,
GeoJSONData* data_ = impl().getData();
- if (!data_) {
- return;
- }
-
if (data_ != data) {
data = data_;
tilePyramid.cache.clear();
- for (auto const& item : tilePyramid.tiles) {
- static_cast<GeoJSONTile*>(item.second.get())->updateData(data->getTile(item.first.canonical));
+ if (data) {
+ const uint8_t maxZ = impl().getZoomRange().max;
+ for (const auto& pair : tilePyramid.tiles) {
+ if (pair.first.canonical.z <= maxZ) {
+ static_cast<GeoJSONTile*>(pair.second.get())->updateData(data->getTile(pair.first.canonical));
+ }
+ }
}
}
+ if (!data) {
+ tilePyramid.tiles.clear();
+ tilePyramid.renderTiles.clear();
+ return;
+ }
+
tilePyramid.update(layers,
needsRendering,
needsRelayout,
@@ -59,35 +67,32 @@ void RenderGeoJSONSource::update(Immutable<style::Source::Impl> baseImpl_,
});
}
-void RenderGeoJSONSource::startRender(Painter& painter) {
- painter.clipIDGenerator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(painter);
+void RenderGeoJSONSource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderGeoJSONSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+void RenderGeoJSONSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-std::map<UnwrappedTileID, RenderTile>& RenderGeoJSONSource::getRenderTiles() {
+std::vector<std::reference_wrapper<RenderTile>> RenderGeoJSONSource::getRenderTiles() {
return tilePyramid.getRenderTiles();
}
std::unordered_map<std::string, std::vector<Feature>>
RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const {
return tilePyramid.querySourceFeatures(options);
}
-void RenderGeoJSONSource::setCacheSize(size_t size) {
- tilePyramid.setCacheSize(size);
-}
-
void RenderGeoJSONSource::onLowMemory() {
tilePyramid.onLowMemory();
}
diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp
index 9b5477e1d0..55166ea901 100644
--- a/src/mbgl/renderer/sources/render_geojson_source.hpp
+++ b/src/mbgl/renderer/sources/render_geojson_source.hpp
@@ -22,21 +22,21 @@ public:
bool needsRelayout,
const TileParameters&) final;
- void startRender(Painter&) final;
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() 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 RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex&) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
@@ -44,12 +44,12 @@ private:
const style::GeoJSONSource::Impl& impl() const;
TilePyramid tilePyramid;
- style::GeoJSONData* data;
+ style::GeoJSONData* data = nullptr;
};
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 f5068b9d7f..d215dc8d13 100644
--- a/src/mbgl/renderer/sources/render_image_source.cpp
+++ b/src/mbgl/renderer/sources/render_image_source.cpp
@@ -1,19 +1,23 @@
#include <mbgl/map/transform_state.hpp>
#include <mbgl/math/log2.hpp>
#include <mbgl/renderer/buckets/raster_bucket.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/sources/render_image_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/util/logging.hpp>
+#include <mbgl/util/constants.hpp>
namespace mbgl {
using namespace style;
RenderImageSource::RenderImageSource(Immutable<style::ImageSource::Impl> impl_)
- : RenderSource(impl_), shouldRender(false) {
+ : RenderSource(impl_) {
}
RenderImageSource::~RenderImageSource() = default;
@@ -26,7 +30,7 @@ bool RenderImageSource::isLoaded() const {
return !!bucket;
}
-void RenderImageSource::startRender(Painter& painter) {
+void RenderImageSource::startRender(PaintParameters& parameters) {
if (!isLoaded()) {
return;
}
@@ -36,31 +40,53 @@ void RenderImageSource::startRender(Painter& painter) {
for (size_t i = 0; i < tileIds.size(); i++) {
mat4 matrix;
matrix::identity(matrix);
- painter.state.matrixFor(matrix, tileIds[i]);
- matrix::multiply(matrix, painter.projMatrix, matrix);
+ parameters.state.matrixFor(matrix, tileIds[i]);
+ matrix::multiply(matrix, parameters.projMatrix, matrix);
matrices.push_back(matrix);
}
- if (bucket->needsUpload() && shouldRender) {
- bucket->upload(painter.context);
+ if (bucket->needsUpload()) {
+ bucket->upload(parameters.context);
}
}
-void RenderImageSource::finishRender(Painter& painter) {
- if (!isLoaded() || !shouldRender) {
+void RenderImageSource::finishRender(PaintParameters& parameters) {
+ if (!isLoaded() || !(parameters.debugOptions & MapDebugOptions::TileBorders)) {
return;
}
+
+ static const style::Properties<>::PossiblyEvaluated properties {};
+ static const DebugProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+
for (auto matrix : matrices) {
- painter.renderTileDebug(matrix);
+ parameters.programs.debug.draw(
+ parameters.context,
+ gl::LineStrip { 4.0f * parameters.pixelRatio },
+ gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(),
+ gl::ColorMode::unblended(),
+ DebugProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_color::Value{ Color::red() }
+ },
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.tileBorderIndexBuffer,
+ parameters.staticData.tileBorderSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ "debug"
+ );
}
}
std::unordered_map<std::string, std::vector<Feature>>
RenderImageSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions&) const {
- return {};
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions&,
+ const CollisionIndex&) const {
+ return std::unordered_map<std::string, std::vector<Feature>> {};
}
std::vector<Feature> RenderImageSource::querySourceFeatures(const SourceQueryOptions&) const {
@@ -72,89 +98,106 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
const bool needsRendering,
const bool,
const TileParameters& parameters) {
- std::swap(baseImpl, baseImpl_);
-
enabled = needsRendering;
+ if (!needsRendering) {
+ return;
+ }
auto transformState = parameters.transformState;
- auto size = transformState.getSize();
- double viewportHeight = size.height;
+ std::swap(baseImpl, baseImpl_);
auto coords = impl().getCoordinates();
+ std::shared_ptr<PremultipliedImage> image = impl().getImage();
- // Compute the screen coordinates at wrap=0 for the given LatLng
- ScreenCoordinate nePixel = { -INFINITY, -INFINITY };
- ScreenCoordinate swPixel = { INFINITY, INFINITY };
-
- 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);
- }
- double width = nePixel.x - swPixel.x;
- double height = nePixel.y - swPixel.y;
-
- // Don't bother drawing the ImageSource unless it occupies >4 screen pixels
- shouldRender = (width * height > 4);
- if (!shouldRender) {
+ if (!image || !image->valid()) {
+ enabled = false;
return;
}
+ // 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) {
+ 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);
+ }
+
// Calculate the optimum zoom level to determine the tile ids to use for transforms
- double minScale = INFINITY;
- if (width > 0 || height > 0) {
- double scaleX = double(size.width) / width;
- double scaleY = double(size.height) / height;
- minScale = util::min(scaleX, scaleY);
+ 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;
}
- double zoom = transformState.getZoom() + util::log2(minScale);
- zoom = 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, ::floor(zoom));
+ auto tileCover = util::tileCover(imageBounds, zoom);
tileIds.clear();
tileIds.push_back(tileCover[0]);
+ bool hasVisibleTile = false;
// Add additional wrapped tile ids if neccessary
auto idealTiles = util::tileCover(transformState, transformState.getZoom());
for (auto tile : idealTiles) {
if (tile.wrap != 0 && tileCover[0].canonical.isChildOf(tile.canonical)) {
tileIds.push_back({ tile.wrap, tileCover[0].canonical });
+ hasVisibleTile = true;
+ }
+ else if (!hasVisibleTile) {
+ for (auto coveringTile: tileCover) {
+ if(coveringTile.canonical == tile.canonical ||
+ coveringTile.canonical.isChildOf(tile.canonical) ||
+ tile.canonical.isChildOf(coveringTile.canonical)) {
+ hasVisibleTile = true;
+ }
+ }
}
}
+ enabled = hasVisibleTile;
+ if (!enabled) {
+ return;
+ }
+
// 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);
}
-
- const UnassociatedImage& image = impl().getImage();
- if (!image.valid()) {
- return;
- }
-
- if (!bucket || image != bucket->image) {
- bucket = std::make_unique<RasterBucket>(image.clone());
+ if (!bucket) {
+ bucket = std::make_unique<RasterBucket>(image);
} else {
bucket->clear();
+ if (image != bucket->image) {
+ bucket->setImage(image);
+ }
}
// Set Bucket Vertices, Indices, and segments
bucket->vertices.emplace_back(
RasterProgram::layoutVertex({ geomCoords[0].x, geomCoords[0].y }, { 0, 0 }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { 32767, 0 }));
+ RasterProgram::layoutVertex({ geomCoords[1].x, geomCoords[1].y }, { util::EXTENT, 0 }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, 32767 }));
+ RasterProgram::layoutVertex({ geomCoords[3].x, geomCoords[3].y }, { 0, util::EXTENT }));
bucket->vertices.emplace_back(
- RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { 32767, 32767 }));
+ RasterProgram::layoutVertex({ geomCoords[2].x, geomCoords[2].y }, { util::EXTENT, util::EXTENT }));
bucket->indices.emplace_back(0, 1, 2);
bucket->indices.emplace_back(1, 2, 3);
@@ -162,16 +205,6 @@ void RenderImageSource::update(Immutable<style::Source::Impl> baseImpl_,
bucket->segments.emplace_back(0, 0, 4, 6);
}
-void RenderImageSource::render(Painter& painter,
- PaintParameters& parameters,
- const RenderLayer& layer) {
- if (isLoaded() && !bucket->needsUpload() && shouldRender) {
- for (auto matrix : matrices) {
- bucket->render(painter, parameters, layer, matrix);
- }
- }
-}
-
void RenderImageSource::dumpDebugLogs() const {
Log::Info(Event::General, "RenderImageSource::id: %s", impl().id.c_str());
Log::Info(Event::General, "RenderImageSource::loaded: %s", isLoaded() ? "yes" : "no");
diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp
index 5175cbf4a4..72cf4cea61 100644
--- a/src/mbgl/renderer/sources/render_image_source.hpp
+++ b/src/mbgl/renderer/sources/render_image_source.hpp
@@ -5,13 +5,8 @@
#include <mbgl/style/sources/image_source_impl.hpp>
namespace mbgl {
-class RenderLayer;
-class PaintParameters;
-class RasterBucket;
-namespace gl {
-class Context;
-} // namespace gl
+class RasterBucket;
class RenderImageSource : public RenderSource {
public:
@@ -20,9 +15,8 @@ public:
bool isLoaded() const final;
- void startRender(Painter&) final;
- void render(Painter&, PaintParameters&, const RenderLayer&);
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
void update(Immutable<style::Source::Impl>,
const std::vector<Immutable<style::Layer::Impl>>&,
@@ -30,37 +24,36 @@ public:
bool needsRelayout,
const TileParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() final {
- return tiles;
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles() final {
+ return {};
}
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final {
- }
void onLowMemory() final {
}
void dumpDebugLogs() const final;
private:
+ friend class RenderRasterLayer;
+
const style::ImageSource::Impl& impl() const;
- std::map<UnwrappedTileID, RenderTile> tiles;
std::vector<UnwrappedTileID> tileIds;
std::unique_ptr<RasterBucket> bucket;
std::vector<mat4> matrices;
- bool shouldRender;
};
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_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp
index 385437af1d..f11f9b7aed 100644
--- a/src/mbgl/renderer/sources/render_raster_source.cpp
+++ b/src/mbgl/renderer/sources/render_raster_source.cpp
@@ -1,6 +1,7 @@
#include <mbgl/renderer/sources/render_raster_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/tile/raster_tile.hpp>
+#include <mbgl/algorithm/update_tile_masks.hpp>
namespace mbgl {
@@ -56,34 +57,32 @@ void RenderRasterSource::update(Immutable<style::Source::Impl> baseImpl_,
});
}
-void RenderRasterSource::startRender(Painter& painter) {
- tilePyramid.startRender(painter);
+void RenderRasterSource::startRender(PaintParameters& parameters) {
+ algorithm::updateTileMasks(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderRasterSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+void RenderRasterSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-std::map<UnwrappedTileID, RenderTile>& RenderRasterSource::getRenderTiles() {
+std::vector<std::reference_wrapper<RenderTile>> RenderRasterSource::getRenderTiles() {
return tilePyramid.getRenderTiles();
}
std::unordered_map<std::string, std::vector<Feature>>
RenderRasterSource::queryRenderedFeatures(const ScreenLineString&,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions&) const {
- return {};
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions&,
+ const CollisionIndex& ) const {
+ return std::unordered_map<std::string, std::vector<Feature>> {};
}
std::vector<Feature> RenderRasterSource::querySourceFeatures(const SourceQueryOptions&) const {
return {};
}
-void RenderRasterSource::setCacheSize(size_t size) {
- tilePyramid.setCacheSize(size);
-}
-
void RenderRasterSource::onLowMemory() {
tilePyramid.onLowMemory();
}
diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp
index d1e37a3099..25041fde43 100644
--- a/src/mbgl/renderer/sources/render_raster_source.hpp
+++ b/src/mbgl/renderer/sources/render_raster_source.hpp
@@ -18,21 +18,21 @@ public:
bool needsRelayout,
const TileParameters&) final;
- void startRender(Painter&) final;
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() 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 RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
@@ -45,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 5b266b10a5..49f8fdff2c 100644
--- a/src/mbgl/renderer/sources/render_vector_source.cpp
+++ b/src/mbgl/renderer/sources/render_vector_source.cpp
@@ -1,6 +1,6 @@
#include <mbgl/renderer/sources/render_vector_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/tile/vector_tile.hpp>
#include <mbgl/algorithm/generate_clip_ids.hpp>
@@ -60,35 +60,32 @@ void RenderVectorSource::update(Immutable<style::Source::Impl> baseImpl_,
});
}
-void RenderVectorSource::startRender(Painter& painter) {
- painter.clipIDGenerator.update(tilePyramid.getRenderTiles());
- tilePyramid.startRender(painter);
+void RenderVectorSource::startRender(PaintParameters& parameters) {
+ parameters.clipIDGenerator.update(tilePyramid.getRenderTiles());
+ tilePyramid.startRender(parameters);
}
-void RenderVectorSource::finishRender(Painter& painter) {
- tilePyramid.finishRender(painter);
+void RenderVectorSource::finishRender(PaintParameters& parameters) {
+ tilePyramid.finishRender(parameters);
}
-std::map<UnwrappedTileID, RenderTile>& RenderVectorSource::getRenderTiles() {
+std::vector<std::reference_wrapper<RenderTile>> RenderVectorSource::getRenderTiles() {
return tilePyramid.getRenderTiles();
}
std::unordered_map<std::string, std::vector<Feature>>
RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const {
- return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options);
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex);
}
std::vector<Feature> RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const {
return tilePyramid.querySourceFeatures(options);
}
-void RenderVectorSource::setCacheSize(size_t size) {
- tilePyramid.setCacheSize(size);
-}
-
void RenderVectorSource::onLowMemory() {
tilePyramid.onLowMemory();
}
diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp
index d5d9598a75..4a992e854f 100644
--- a/src/mbgl/renderer/sources/render_vector_source.hpp
+++ b/src/mbgl/renderer/sources/render_vector_source.hpp
@@ -18,21 +18,21 @@ public:
bool needsRelayout,
const TileParameters&) final;
- void startRender(Painter&) final;
- void finishRender(Painter&) final;
+ void startRender(PaintParameters&) final;
+ void finishRender(PaintParameters&) final;
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles() 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 RenderStyle& style,
- const RenderedQueryOptions& options) const final;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const final;
std::vector<Feature>
querySourceFeatures(const SourceQueryOptions&) const final;
- void setCacheSize(size_t) final;
void onLowMemory() final;
void dumpDebugLogs() const final;
@@ -45,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_mask.hpp b/src/mbgl/renderer/tile_mask.hpp
new file mode 100644
index 0000000000..5f24d63ba4
--- /dev/null
+++ b/src/mbgl/renderer/tile_mask.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <mbgl/tile/tile_id.hpp>
+
+#include <set>
+
+namespace mbgl {
+
+// A TileMask is a set of TileIDs that describe what part of a tile should be rendered. It omits
+// those parts of the tile that are covered by other/better tiles. If the entire tile should be
+// rendered, it contains the { 0, 0, 0 } tile. If it's empty, no part of the tile will be rendered.
+// TileMasks are typically generated with algorithm::updateTileMasks().
+using TileMask = std::set<CanonicalTileID>;
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/tile_parameters.hpp b/src/mbgl/renderer/tile_parameters.hpp
index cf7a5b100a..665c7490d2 100644
--- a/src/mbgl/renderer/tile_parameters.hpp
+++ b/src/mbgl/renderer/tile_parameters.hpp
@@ -13,8 +13,8 @@ class GlyphManager;
class TileParameters {
public:
- float pixelRatio;
- MapDebugOptions debugOptions;
+ const float pixelRatio;
+ const MapDebugOptions debugOptions;
const TransformState& transformState;
Scheduler& workerScheduler;
FileSource& fileSource;
@@ -22,6 +22,7 @@ public:
AnnotationManager& annotationManager;
ImageManager& imageManager;
GlyphManager& glyphManager;
+ const uint8_t prefetchZoomDelta;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp
index c2806299e3..870d9050bc 100644
--- a/src/mbgl/renderer/tile_pyramid.cpp
+++ b/src/mbgl/renderer/tile_pyramid.cpp
@@ -1,11 +1,10 @@
#include <mbgl/renderer/tile_pyramid.hpp>
#include <mbgl/renderer/render_tile.hpp>
-#include <mbgl/renderer/painter.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/map/query.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/math/clamp.hpp>
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/enum.hpp>
@@ -39,23 +38,20 @@ bool TilePyramid::isLoaded() const {
return true;
}
-void TilePyramid::startRender(Painter& painter) {
- for (auto& pair : renderTiles) {
- pair.second.startRender(painter);
+void TilePyramid::startRender(PaintParameters& parameters) {
+ for (auto& tile : renderTiles) {
+ tile.startRender(parameters);
}
}
-void TilePyramid::finishRender(Painter& painter) {
- for (auto& pair : renderTiles) {
- auto& tile = pair.second;
- if (tile.used) {
- painter.renderTileDebug(tile);
- }
+void TilePyramid::finishRender(PaintParameters& parameters) {
+ for (auto& tile : renderTiles) {
+ tile.finishRender(parameters);
}
}
-std::map<UnwrappedTileID, RenderTile>& TilePyramid::getRenderTiles() {
- return renderTiles;
+std::vector<std::reference_wrapper<RenderTile>> TilePyramid::getRenderTiles() {
+ return { renderTiles.begin(), renderTiles.end() };
}
void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layers,
@@ -89,14 +85,27 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
// Determine the overzooming/underzooming amounts and required tiles.
int32_t overscaledZoom = util::coveringZoomLevel(parameters.transformState.getZoom(), type, tileSize);
int32_t tileZoom = overscaledZoom;
+ int32_t panZoom = zoomRange.max;
std::vector<UnwrappedTileID> idealTiles;
+ std::vector<UnwrappedTileID> panTiles;
+
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) {
+ // Only attempt prefetching in continuous mode.
+ if (parameters.mode == MapMode::Continuous) {
tileZoom = idealZoom;
+
+ // 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) {
+ panTiles = util::tileCover(parameters.transformState, panZoom);
+ }
}
idealTiles = util::tileCover(parameters.transformState, idealZoom);
@@ -107,10 +116,13 @@ 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) {
+ tile.setNecessity(necessity);
+ }
- auto retainTileFn = [&](Tile& tile, Resource::Necessity necessity) -> void {
- retain.emplace(tile.id);
- tile.setNecessity(necessity);
if (needsRelayout) {
tile.setLayers(layers);
}
@@ -133,13 +145,40 @@ 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(tileID, RenderTile{ tileID, 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();
+
+ if (!panTiles.empty()) {
+ algorithm::updateRenderables(getTileFn, createTileFn, retainTileFn,
+ [](const UnwrappedTileID&, Tile&) {}, panTiles, zoomRange, panZoom);
+ }
+
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 =
@@ -150,41 +189,44 @@ void TilePyramid::update(const std::vector<Immutable<style::Layer::Impl>>& layer
cache.setSize(conservativeCacheSize);
}
- removeStaleTiles(retain);
-
- const PlacementConfig config { parameters.transformState.getAngle(),
- parameters.transformState.getPitch(),
- parameters.debugOptions & MapDebugOptions::Collision };
-
- for (auto& pair : tiles) {
- pair.second->setPlacementConfig(config);
- }
-}
-
-// Moves all tiles to the cache except for those specified in the retain set.
-void TilePyramid::removeStaleTiles(const std::set<OverscaledTileID>& retain) {
// Remove stale tiles. This goes through the (sorted!) tiles map and retain set in lockstep
// and removes items from tiles that don't have the corresponding key in the retain set.
- auto tilesIt = tiles.begin();
- auto retainIt = retain.begin();
- while (tilesIt != tiles.end()) {
- if (retainIt == retain.end() || tilesIt->first < *retainIt) {
- tilesIt->second->setNecessity(Tile::Necessity::Optional);
- cache.add(tilesIt->first, std::move(tilesIt->second));
- tiles.erase(tilesIt++);
- } else {
- if (!(*retainIt < tilesIt->first)) {
- ++tilesIt;
+ {
+ auto tilesIt = tiles.begin();
+ auto retainIt = retain.begin();
+ while (tilesIt != tiles.end()) {
+ auto renderedIt = rendered.find(tilesIt->first.toUnwrapped());
+ if (renderedIt == rendered.end()) {
+ // Since this tile isn't in the render set, crossTileIDs won't be kept
+ // updated by CrossTileSymbolIndex. We need to reset the stored crossTileIDs
+ // so they're not reused if/when this tile is re-added to the render set
+ tilesIt->second->resetCrossTileIDs();
+ }
+ if (retainIt == retain.end() || tilesIt->first < *retainIt) {
+ if (!needsRelayout) {
+ tilesIt->second->setNecessity(TileNecessity::Optional);
+ cache.add(tilesIt->first, std::move(tilesIt->second));
+ }
+ tiles.erase(tilesIt++);
+ } else {
+ if (!(*retainIt < tilesIt->first)) {
+ ++tilesIt;
+ }
+ ++retainIt;
}
- ++retainIt;
}
}
+
+ for (auto& pair : tiles) {
+ pair.second->setShowCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision);
+ }
}
std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const {
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
return result;
@@ -199,18 +241,14 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry);
-
- auto sortRenderTiles = [](const RenderTile& a, const RenderTile& b) {
+ std::vector<std::reference_wrapper<const RenderTile>> sortedTiles{ renderTiles.begin(),
+ renderTiles.end() };
+ std::sort(sortedTiles.begin(), sortedTiles.end(), [](const RenderTile& a, const RenderTile& b) {
return std::tie(a.id.canonical.z, a.id.canonical.y, a.id.wrap, a.id.canonical.x) <
std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x);
- };
- std::vector<std::reference_wrapper<const RenderTile>> sortedTiles;
- std::transform(renderTiles.cbegin(), renderTiles.cend(), std::back_inserter(sortedTiles),
- [](const auto& pair) { return std::ref(pair.second); });
- std::sort(sortedTiles.begin(), sortedTiles.end(), sortRenderTiles);
+ });
- for (const auto& renderTileRef : sortedTiles) {
- const RenderTile& renderTile = renderTileRef.get();
+ for (const RenderTile& renderTile : sortedTiles) {
GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min);
if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) {
continue;
@@ -230,8 +268,9 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
renderTile.tile.queryRenderedFeatures(result,
tileSpaceQueryGeometry,
transformState,
- style,
- options);
+ layers,
+ options,
+ collisionIndex);
}
return result;
diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp
index 5846560808..feab8a838c 100644
--- a/src/mbgl/renderer/tile_pyramid.hpp
+++ b/src/mbgl/renderer/tile_pyramid.hpp
@@ -18,10 +18,10 @@
namespace mbgl {
-class Painter;
+class PaintParameters;
class TransformState;
class RenderTile;
-class RenderStyle;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
class TileParameters;
@@ -37,21 +37,22 @@ public:
bool needsRendering,
bool needsRelayout,
const TileParameters&,
- SourceType type,
+ style::SourceType type,
uint16_t tileSize,
Range<uint8_t> zoomRange,
std::function<std::unique_ptr<Tile> (const OverscaledTileID&)> createTile);
- void startRender(Painter&);
- void finishRender(Painter&);
+ void startRender(PaintParameters&);
+ void finishRender(PaintParameters&);
- std::map<UnwrappedTileID, RenderTile>& getRenderTiles();
+ std::vector<std::reference_wrapper<RenderTile>> getRenderTiles();
std::unordered_map<std::string, std::vector<Feature>>
queryRenderedFeatures(const ScreenLineString& geometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) const;
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) const;
std::vector<Feature> querySourceFeatures(const SourceQueryOptions&) const;
@@ -63,12 +64,10 @@ public:
bool enabled = false;
- void removeStaleTiles(const std::set<OverscaledTileID>&);
-
std::map<OverscaledTileID, std::unique_ptr<Tile>> tiles;
TileCache cache;
- std::map<UnwrappedTileID, RenderTile> renderTiles;
+ std::vector<RenderTile> renderTiles;
TileObserver* observer = nullptr;
};
diff --git a/src/mbgl/renderer/update_parameters.hpp b/src/mbgl/renderer/update_parameters.hpp
index ce79a4f31b..b54abc050d 100644
--- a/src/mbgl/renderer/update_parameters.hpp
+++ b/src/mbgl/renderer/update_parameters.hpp
@@ -2,20 +2,22 @@
#include <mbgl/map/mode.hpp>
#include <mbgl/map/transform_state.hpp>
-#include <mbgl/util/chrono.hpp>
#include <mbgl/style/light.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/source.hpp>
#include <mbgl/style/layer.hpp>
+#include <mbgl/util/chrono.hpp>
+#include <mbgl/util/immutable.hpp>
+
+#include <vector>
namespace mbgl {
-class Scheduler;
-class FileSource;
class AnnotationManager;
class UpdateParameters {
public:
+ const bool styleLoaded;
const MapMode mode;
const float pixelRatio;
const MapDebugOptions debugOptions;
@@ -30,9 +32,12 @@ public:
const Immutable<std::vector<Immutable<style::Source::Impl>>> sources;
const Immutable<std::vector<Immutable<style::Layer::Impl>>> layers;
- Scheduler& scheduler;
- FileSource& fileSource;
AnnotationManager& annotationManager;
+
+ const uint8_t prefetchZoomDelta;
+
+ // For still image requests, render requested
+ const bool stillImageRequest;
};
} // namespace mbgl
diff --git a/src/mbgl/shaders/circle.cpp b/src/mbgl/shaders/circle.cpp
index 2e0c76122c..c14335914b 100644
--- a/src/mbgl/shaders/circle.cpp
+++ b/src/mbgl/shaders/circle.cpp
@@ -9,7 +9,9 @@ const char* circle::name = "circle";
const char* circle::vertexSource = R"MBGL_SHADER(
uniform mat4 u_matrix;
uniform bool u_scale_with_map;
+uniform bool u_pitch_with_map;
uniform vec2 u_extrude_scale;
+uniform highp float u_camera_to_center_distance;
attribute vec2 a_pos;
@@ -22,6 +24,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
uniform lowp float a_radius_t;
attribute mediump vec2 a_radius;
@@ -30,6 +33,7 @@ varying mediump float radius;
uniform mediump float u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -38,6 +42,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -46,6 +51,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
uniform lowp float a_stroke_color_t;
attribute highp vec4 a_stroke_color;
@@ -54,6 +60,7 @@ varying highp vec4 stroke_color;
uniform highp vec4 u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
uniform lowp float a_stroke_width_t;
attribute mediump vec2 a_stroke_width;
@@ -62,6 +69,7 @@ varying mediump float stroke_width;
uniform mediump float u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
uniform lowp float a_stroke_opacity_t;
attribute lowp vec2 a_stroke_opacity;
@@ -70,63 +78,87 @@ varying lowp float stroke_opacity;
uniform lowp float u_stroke_opacity;
#endif
+
varying vec3 v_data;
void main(void) {
-
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
radius = unpack_mix_vec2(a_radius, a_radius_t);
#else
mediump float radius = u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
stroke_color = unpack_mix_vec4(a_stroke_color, a_stroke_color_t);
#else
highp vec4 stroke_color = u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
stroke_width = unpack_mix_vec2(a_stroke_width, a_stroke_width_t);
#else
mediump float stroke_width = u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
stroke_opacity = unpack_mix_vec2(a_stroke_opacity, a_stroke_opacity_t);
#else
lowp float stroke_opacity = u_stroke_opacity;
#endif
+
// unencode the extrusion vector that we snuck into the a_pos vector
vec2 extrude = vec2(mod(a_pos, 2.0) * 2.0 - 1.0);
// multiply a_pos by 0.5, since we had it * 2 in order to sneak
// in extrusion data
- gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0, 1);
-
- if (u_scale_with_map) {
- gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale;
+ vec2 circle_center = floor(a_pos * 0.5);
+ if (u_pitch_with_map) {
+ vec2 corner_position = circle_center;
+ if (u_scale_with_map) {
+ corner_position += extrude * (radius + stroke_width) * u_extrude_scale;
+ } else {
+ // Pitching the circle with the map effectively scales it with the map
+ // To counteract the effect for pitch-scale: viewport, we rescale the
+ // whole circle based on the pitch scaling effect at its central point
+ vec4 projected_center = u_matrix * vec4(circle_center, 0, 1);
+ corner_position += extrude * (radius + stroke_width) * u_extrude_scale * (projected_center.w / u_camera_to_center_distance);
+ }
+
+ gl_Position = u_matrix * vec4(corner_position, 0, 1);
} else {
- gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * gl_Position.w;
+ gl_Position = u_matrix * vec4(circle_center, 0, 1);
+
+ if (u_scale_with_map) {
+ gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * u_camera_to_center_distance;
+ } else {
+ gl_Position.xy += extrude * (radius + stroke_width) * u_extrude_scale * gl_Position.w;
+ }
}
// This is a minimum blur distance that serves as a faux-antialiasing for
@@ -146,74 +178,88 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_radius
varying mediump float radius;
#else
uniform mediump float u_radius;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_color
varying highp vec4 stroke_color;
#else
uniform highp vec4 u_stroke_color;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_width
varying mediump float stroke_width;
#else
uniform mediump float u_stroke_width;
#endif
+
#ifndef HAS_UNIFORM_u_stroke_opacity
varying lowp float stroke_opacity;
#else
uniform lowp float u_stroke_opacity;
#endif
+
varying vec3 v_data;
void main() {
-
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_radius
mediump float radius = u_radius;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_color
highp vec4 stroke_color = u_stroke_color;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_width
mediump float stroke_width = u_stroke_width;
#endif
+
#ifdef HAS_UNIFORM_u_stroke_opacity
lowp float stroke_opacity = u_stroke_opacity;
#endif
+
vec2 extrude = v_data.xy;
float extrude_length = length(extrude);
diff --git a/src/mbgl/shaders/collision_box.cpp b/src/mbgl/shaders/collision_box.cpp
index 5f733c6a1e..9d11640bf4 100644
--- a/src/mbgl/shaders/collision_box.cpp
+++ b/src/mbgl/shaders/collision_box.cpp
@@ -8,49 +8,52 @@ namespace shaders {
const char* collision_box::name = "collision_box";
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 vec2 u_extrude_scale;
+uniform float u_camera_to_center_distance;
-varying float v_max_zoom;
-varying float v_placement_zoom;
+varying float v_placed;
+varying float v_notUsed;
void main() {
- gl_Position = u_matrix * vec4(a_pos + a_extrude / u_scale, 0.0, 1.0);
+ vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1);
+ highp float camera_to_anchor_distance = projectedPoint.w;
+ highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance);
- v_max_zoom = a_data.x;
- v_placement_zoom = a_data.y;
+ 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;
+
+ v_placed = a_placed.x;
+ v_notUsed = a_placed.y;
}
)MBGL_SHADER";
const char* collision_box::fragmentSource = R"MBGL_SHADER(
-uniform float u_zoom;
-uniform float u_maxzoom;
-varying float v_max_zoom;
-varying float v_placement_zoom;
+varying float v_placed;
+varying float v_notUsed;
void main() {
float alpha = 0.5;
- gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0) * alpha;
-
- if (v_placement_zoom > u_zoom) {
- gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
- }
+ // Red = collision, hide label
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * alpha;
- if (u_zoom >= v_max_zoom) {
- gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) * alpha * 0.25;
+ // Blue = no collision, label is showing
+ if (v_placed > 0.5) {
+ gl_FragColor = vec4(0.0, 0.0, 1.0, 0.5) * alpha;
}
- 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..1e85d99a33
--- /dev/null
+++ b/src/mbgl/shaders/collision_circle.cpp
@@ -0,0 +1,83 @@
+// 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 = 0.5 + 0.5 * (camera_to_anchor_distance / u_camera_to_center_distance);
+
+ 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(
+
+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 = 3.0;
+ 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 d39dcf25be..9012cfa755 100644
--- a/src/mbgl/shaders/debug.cpp
+++ b/src/mbgl/shaders/debug.cpp
@@ -12,7 +12,7 @@ attribute vec2 a_pos;
uniform mat4 u_matrix;
void main() {
- gl_Position = u_matrix * vec4(a_pos, step(32767.0, a_pos.x), 1);
+ gl_Position = u_matrix * vec4(a_pos, 0, 1);
}
)MBGL_SHADER";
diff --git a/src/mbgl/shaders/fill.cpp b/src/mbgl/shaders/fill.cpp
index 8f5f304014..3ba00836a2 100644
--- a/src/mbgl/shaders/fill.cpp
+++ b/src/mbgl/shaders/fill.cpp
@@ -20,6 +20,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -28,20 +29,23 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
}
@@ -54,22 +58,26 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
gl_FragColor = color * opacity;
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/fill_extrusion.cpp b/src/mbgl/shaders/fill_extrusion.cpp
index ad14e4f32e..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;
@@ -27,6 +26,7 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
uniform lowp float a_height_t;
attribute lowp vec2 a_height;
@@ -36,6 +36,7 @@ uniform lowp float u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
uniform lowp float a_color_t;
attribute highp vec4 a_color;
@@ -44,31 +45,36 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_base
base = unpack_mix_vec2(a_base, a_base_t);
#else
lowp float base = u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
height = unpack_mix_vec2(a_height, a_height_t);
#else
lowp float height = u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#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);
@@ -82,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
@@ -91,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);
}
@@ -113,32 +119,38 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
varying lowp float height;
#else
uniform lowp float u_height;
#endif
+
#ifndef HAS_UNIFORM_u_color
varying highp vec4 color;
#else
uniform highp vec4 u_color;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_base
lowp float base = u_base;
#endif
+
#ifdef HAS_UNIFORM_u_height
lowp float height = u_height;
#endif
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
gl_FragColor = v_color;
#ifdef OVERDRAW_INSPECTOR
diff --git a/src/mbgl/shaders/fill_extrusion_pattern.cpp b/src/mbgl/shaders/fill_extrusion_pattern.cpp
index 2681973af6..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;
@@ -39,6 +38,7 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
uniform lowp float a_height_t;
attribute lowp vec2 a_height;
@@ -47,40 +47,46 @@ varying lowp float height;
uniform lowp float u_height;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_base
base = unpack_mix_vec2(a_base, a_base_t);
#else
lowp float base = u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
height = unpack_mix_vec2(a_height, a_height_t);
#else
lowp float height = u_height;
#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);
}
@@ -109,22 +115,26 @@ varying lowp float base;
uniform lowp float u_base;
#endif
+
#ifndef HAS_UNIFORM_u_height
varying lowp float height;
#else
uniform lowp float u_height;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_base
lowp float base = u_base;
#endif
+
#ifdef HAS_UNIFORM_u_height
lowp float height = u_height;
#endif
+
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);
diff --git a/src/mbgl/shaders/fill_outline.cpp b/src/mbgl/shaders/fill_outline.cpp
index 18a4d8c0a8..9ade598d10 100644
--- a/src/mbgl/shaders/fill_outline.cpp
+++ b/src/mbgl/shaders/fill_outline.cpp
@@ -23,6 +23,7 @@ varying highp vec4 outline_color;
uniform highp vec4 u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -31,20 +32,23 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_outline_color
outline_color = unpack_mix_vec4(a_outline_color, a_outline_color_t);
#else
highp vec4 outline_color = u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world;
}
@@ -58,24 +62,28 @@ varying highp vec4 outline_color;
uniform highp vec4 u_outline_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
varying vec2 v_pos;
void main() {
-
+
#ifdef HAS_UNIFORM_u_outline_color
highp vec4 outline_color = u_outline_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
float dist = length(v_pos - gl_FragCoord.xy);
float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
gl_FragColor = outline_color * (alpha * opacity);
diff --git a/src/mbgl/shaders/fill_outline_pattern.cpp b/src/mbgl/shaders/fill_outline_pattern.cpp
index 68e69c2135..11cddb7d07 100644
--- a/src/mbgl/shaders/fill_outline_pattern.cpp
+++ b/src/mbgl/shaders/fill_outline_pattern.cpp
@@ -32,14 +32,16 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
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);
@@ -70,12 +72,14 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
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);
diff --git a/src/mbgl/shaders/fill_pattern.cpp b/src/mbgl/shaders/fill_pattern.cpp
index f6f9e2fbff..a3817c4426 100644
--- a/src/mbgl/shaders/fill_pattern.cpp
+++ b/src/mbgl/shaders/fill_pattern.cpp
@@ -30,14 +30,16 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
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);
@@ -65,12 +67,14 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
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);
diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp
index 1eb92c4b71..c700295a15 100644
--- a/src/mbgl/shaders/line.cpp
+++ b/src/mbgl/shaders/line.cpp
@@ -21,7 +21,7 @@ const char* line::vertexSource = R"MBGL_SHADER(
// #define scale 63.0
#define scale 0.015873016
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -41,6 +41,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -49,6 +50,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -57,6 +59,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -64,6 +67,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -71,6 +75,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -78,61 +83,66 @@ attribute mediump vec2 a_width;
uniform mediump float u_width;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
mediump float width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
-
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
- offset = -1.0 * offset;
+ offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -149,9 +159,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -172,36 +179,42 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
varying vec2 v_width2;
varying vec2 v_normal;
varying float v_gamma_scale;
void main() {
-
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp
index 222042a13c..f8d785ade9 100644
--- a/src/mbgl/shaders/line_pattern.cpp
+++ b/src/mbgl/shaders/line_pattern.cpp
@@ -23,7 +23,7 @@ const char* line_pattern::vertexSource = R"MBGL_SHADER(
// Retina devices need a smaller distance to avoid aliasing.
#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -44,6 +44,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -52,6 +53,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -59,6 +61,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -66,6 +69,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -73,55 +77,60 @@ attribute mediump vec2 a_width;
uniform mediump float u_width;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_width
mediump float width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
- offset = -1.0 * offset;
+ offset = -1.0 * offset;
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -138,9 +147,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -178,22 +184,26 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/line_sdf.cpp b/src/mbgl/shaders/line_sdf.cpp
index 168f4ca98d..c5d50566e8 100644
--- a/src/mbgl/shaders/line_sdf.cpp
+++ b/src/mbgl/shaders/line_sdf.cpp
@@ -23,7 +23,7 @@ const char* line_sdf::vertexSource = R"MBGL_SHADER(
// Retina devices need a smaller distance to avoid aliasing.
#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0
-attribute vec2 a_pos;
+attribute vec4 a_pos_normal;
attribute vec4 a_data;
uniform mat4 u_matrix;
@@ -49,6 +49,7 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
uniform lowp float a_blur_t;
attribute lowp vec2 a_blur;
@@ -57,6 +58,7 @@ varying lowp float blur;
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -65,6 +67,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
uniform lowp float a_gapwidth_t;
attribute mediump vec2 a_gapwidth;
@@ -72,6 +75,7 @@ attribute mediump vec2 a_gapwidth;
uniform mediump float u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
uniform lowp float a_offset_t;
attribute lowp vec2 a_offset;
@@ -79,6 +83,7 @@ attribute lowp vec2 a_offset;
uniform lowp float u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
uniform lowp float a_width_t;
attribute mediump vec2 a_width;
@@ -87,6 +92,7 @@ varying mediump float width;
uniform mediump float u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
uniform lowp float a_floorwidth_t;
attribute lowp vec2 a_floorwidth;
@@ -95,68 +101,75 @@ varying lowp float floorwidth;
uniform lowp float u_floorwidth;
#endif
-void main() {
+void main() {
+
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_vec4(a_color, a_color_t);
#else
highp vec4 color = u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_vec2(a_blur, a_blur_t);
#else
lowp float blur = u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_gapwidth
mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t);
#else
mediump float gapwidth = u_gapwidth;
#endif
+
#ifndef HAS_UNIFORM_u_offset
lowp float offset = unpack_mix_vec2(a_offset, a_offset_t);
#else
lowp float offset = u_offset;
#endif
+
#ifndef HAS_UNIFORM_u_width
width = unpack_mix_vec2(a_width, a_width_t);
#else
mediump float width = u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
floorwidth = unpack_mix_vec2(a_floorwidth, a_floorwidth_t);
#else
lowp float floorwidth = u_floorwidth;
#endif
+
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
float a_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * LINE_DISTANCE_SCALE;
- // We store the texture normals in the most insignificant bit
- // transform y so that 0 => -1 and 1 => 1
- // In the texture normal, x is 0 if the normal points straight up/down and 1 if it's a round cap
+ vec2 pos = a_pos_normal.xy;
+
+ // x is 1 if it's a round cap, 0 otherwise
// y is 1 if the normal points up, and -1 if it points down
- mediump vec2 normal = mod(a_pos, 2.0);
- normal.y = sign(normal.y - 0.5);
+ mediump vec2 normal = a_pos_normal.zw;
v_normal = normal;
- // these transformations used to be applied in the JS and native code bases.
- // moved them into the shader for clarity and simplicity.
+ // these transformations used to be applied in the JS and native code bases.
+ // moved them into the shader for clarity and simplicity.
gapwidth = gapwidth / 2.0;
float halfwidth = width / 2.0;
offset = -1.0 * offset;
-
+
float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0);
float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING;
@@ -172,9 +185,6 @@ void main() {
mediump float t = 1.0 - abs(u);
mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t);
- // Remove the texture normal bit to get the position
- vec2 pos = floor(a_pos * 0.5);
-
vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0);
gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude;
@@ -209,52 +219,62 @@ varying highp vec4 color;
uniform highp vec4 u_color;
#endif
+
#ifndef HAS_UNIFORM_u_blur
varying lowp float blur;
#else
uniform lowp float u_blur;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_width
varying mediump float width;
#else
uniform mediump float u_width;
#endif
+
#ifndef HAS_UNIFORM_u_floorwidth
varying lowp float floorwidth;
#else
uniform lowp float u_floorwidth;
#endif
-void main() {
+void main() {
+
#ifdef HAS_UNIFORM_u_color
highp vec4 color = u_color;
#endif
+
#ifdef HAS_UNIFORM_u_blur
lowp float blur = u_blur;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_width
mediump float width = u_width;
#endif
+
#ifdef HAS_UNIFORM_u_floorwidth
lowp float floorwidth = u_floorwidth;
#endif
+
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * v_width2.s;
diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp
index 95fa624e8d..6baa488a10 100644
--- a/src/mbgl/shaders/preludes.cpp
+++ b/src/mbgl/shaders/preludes.cpp
@@ -24,25 +24,6 @@ precision highp float;
#endif
-float evaluate_zoom_function_1(const vec4 values, const float t) {
- if (t < 1.0) {
- return mix(values[0], values[1], t);
- } else if (t < 2.0) {
- return mix(values[1], values[2], t - 1.0);
- } else {
- return mix(values[2], values[3], t - 2.0);
- }
-}
-vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) {
- if (t < 1.0) {
- return mix(value0, value1, t);
- } else if (t < 2.0) {
- return mix(value1, value2, t - 1.0);
- } else {
- return mix(value2, value3, t - 2.0);
- }
-}
-
// Unpack a pair of values that have been packed into a single float.
// The packed values are assumed to be 8-bit unsigned integers, and are
// packed like so:
@@ -53,9 +34,13 @@ 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 in the mapbox-gl-native shaders,
-// we encode a 4-component color into a pair of floats (i.e. a vec2) as follows:
+// To minimize the number of attributes needed, we encode a 4-component
+// color into a pair of floats (i.e. a vec2) as follows:
// [ floor(color.r * 255) * 256 + color.g * 255,
// floor(color.b * 255) * 256 + color.g * 255 ]
vec4 decode_color(const vec2 encodedColor) {
diff --git a/src/mbgl/shaders/raster.cpp b/src/mbgl/shaders/raster.cpp
index eb7a2db240..98291bfec6 100644
--- a/src/mbgl/shaders/raster.cpp
+++ b/src/mbgl/shaders/raster.cpp
@@ -20,7 +20,12 @@ varying vec2 v_pos1;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
- v_pos0 = (((a_texture_pos / 32767.0) - 0.5) / u_buffer_scale ) + 0.5;
+ // 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.
+ v_pos0 = (((a_texture_pos / 8192.0) - 0.5) / u_buffer_scale ) + 0.5;
v_pos1 = (v_pos0 * u_scale_parent) + u_tl_parent;
}
@@ -45,6 +50,12 @@ void main() {
// read and cross-fade colors from the main and parent tiles
vec4 color0 = texture2D(u_image0, v_pos0);
vec4 color1 = texture2D(u_image1, v_pos1);
+ if (color0.a > 0.0) {
+ color0.rgb = color0.rgb / color0.a;
+ }
+ if (color1.a > 0.0) {
+ color1.rgb = color1.rgb / color1.a;
+ }
vec4 color = mix(color0, color1, u_fade_t);
color.a *= u_opacity;
vec3 rgb = color.rgb;
diff --git a/src/mbgl/shaders/shaders.cpp b/src/mbgl/shaders/shaders.cpp
index 31ff405f02..2e5a318024 100644
--- a/src/mbgl/shaders/shaders.cpp
+++ b/src/mbgl/shaders/shaders.cpp
@@ -22,6 +22,7 @@ std::string programIdentifier(const std::string& vertexSource, const std::string
ss << std::setfill('0') << std::setw(sizeof(size_t) * 2) << std::hex;
ss << std::hash<std::string>()(vertexSource);
ss << std::hash<std::string>()(fragmentSource);
+ ss << "v2";
return ss.str();
}
diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp
index bc570cf361..f5c2bbe22d 100644
--- a/src/mbgl/shaders/symbol_icon.cpp
+++ b/src/mbgl/shaders/symbol_icon.cpp
@@ -7,17 +7,22 @@ namespace shaders {
const char* symbol_icon::name = "symbol_icon";
const char* symbol_icon::vertexSource = R"MBGL_SHADER(
+const float PI = 3.141592653589793;
attribute vec4 a_pos_offset;
attribute vec4 a_data;
+attribute vec3 a_projected_pos;
+attribute float a_fade_opacity;
-// icon-size data (see symbol_sdf.vertex.glsl for more)
-attribute vec3 a_size;
uniform bool u_is_size_zoom_constant;
uniform bool u_is_size_feature_constant;
-uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function
-uniform mediump float u_size; // used when size is both zoom and feature constant
-uniform mediump float u_layout_size; // used when size is feature constant
+uniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function
+uniform highp float u_size; // used when size is both zoom and feature constant
+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 float u_fade_change;
#ifndef HAS_UNIFORM_u_opacity
@@ -28,84 +33,86 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
-// matrix is for the vertex position.
+
uniform mat4 u_matrix;
+uniform mat4 u_label_plane_matrix;
+uniform mat4 u_gl_coord_matrix;
uniform bool u_is_text;
-uniform mediump float u_zoom;
-uniform bool u_rotate_with_map;
-uniform vec2 u_extrude_scale;
+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() {
-
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
vec2 a_pos = a_pos_offset.xy;
vec2 a_offset = a_pos_offset.zw;
vec2 a_tex = a_data.xy;
- mediump vec2 label_data = unpack_float(a_data[2]);
- mediump float a_labelminzoom = label_data[0];
- mediump vec2 a_zoom = unpack_float(a_data[3]);
- mediump float a_minzoom = a_zoom[0];
- mediump float a_maxzoom = a_zoom[1];
+ vec2 a_size = a_data.zw;
+
+ highp float segment_angle = -a_projected_pos[2];
float size;
- // In order to accommodate placing labels around corners in
- // symbol-placement: line, each glyph in a label could have multiple
- // "quad"s only one of which should be shown at a given zoom level.
- // The min/max zoom assigned to each quad is based on the font size at
- // the vector tile's zoom level, which might be different than at the
- // currently rendered zoom level if text-size is zoom-dependent.
- // Thus, we compensate for this difference by calculating an adjustment
- // based on the scale of rendered text size relative to layout text size.
- mediump float layoutSize;
if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = mix(a_size[0], a_size[1], u_size_t) / 10.0;
- layoutSize = a_size[2] / 10.0;
} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = a_size[0] / 10.0;
- layoutSize = size;
} else if (!u_is_size_zoom_constant && u_is_size_feature_constant) {
size = u_size;
- layoutSize = u_layout_size;
} else {
size = u_size;
- layoutSize = u_size;
}
+ vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1);
+ highp float camera_to_anchor_distance = projectedPoint.w;
+ // See comments in symbol_sdf.vertex
+ 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;
+
+ size *= perspective_ratio;
+
float fontScale = u_is_text ? size / 24.0 : size;
- mediump float zoomAdjust = log2(size / layoutSize);
- mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0;
- // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise
- mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom));
+ highp float symbol_rotation = 0.0;
+ if (u_rotate_symbol) {
+ // See comments in symbol_sdf.vertex
+ vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1);
- vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0);
- if (u_rotate_with_map) {
- gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1);
- gl_Position.z += z * gl_Position.w;
- } else {
- gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0);
+ vec2 a = projectedPoint.xy / projectedPoint.w;
+ vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w;
+
+ symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x);
}
+ highp float angle_sin = sin(segment_angle + symbol_rotation);
+ highp float angle_cos = cos(segment_angle + symbol_rotation);
+ 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);
+
v_tex = a_tex / u_texsize;
- v_fade_tex = vec2(a_labelminzoom / 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
@@ -114,16 +121,18 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
varying vec2 v_tex;
-varying vec2 v_fade_tex;
+varying float v_fade_opacity;
void main() {
-
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#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 cce6b769a6..441eaf7aac 100644
--- a/src/mbgl/shaders/symbol_sdf.cpp
+++ b/src/mbgl/shaders/symbol_sdf.cpp
@@ -11,6 +11,8 @@ 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.
@@ -18,14 +20,11 @@ attribute vec4 a_data;
// For source functions, we bind only one value per vertex: the value of {text,icon}-size evaluated for the current feature.
// For composite functions:
// [ text-size(lowerZoomStop, feature),
-// text-size(upperZoomStop, feature),
-// layoutSize == text-size(layoutZoomLevel, feature) ]
-attribute vec3 a_size;
+// text-size(upperZoomStop, feature) ]
uniform bool u_is_size_zoom_constant;
uniform bool u_is_size_feature_constant;
-uniform mediump float u_size_t; // used to interpolate between zoom stops when size is a composite function
-uniform mediump float u_size; // used when size is both zoom and feature constant
-uniform mediump float u_layout_size; // used when size is feature constant
+uniform highp float u_size_t; // used to interpolate between zoom stops when size is a composite function
+uniform highp float u_size; // used when size is both zoom and feature constant
#ifndef HAS_UNIFORM_u_fill_color
@@ -36,6 +35,7 @@ varying highp vec4 fill_color;
uniform highp vec4 u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
uniform lowp float a_halo_color_t;
attribute highp vec4 a_halo_color;
@@ -44,6 +44,7 @@ varying highp vec4 halo_color;
uniform highp vec4 u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
uniform lowp float a_opacity_t;
attribute lowp vec2 a_opacity;
@@ -52,6 +53,7 @@ varying lowp float opacity;
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
uniform lowp float a_halo_width_t;
attribute lowp vec2 a_halo_width;
@@ -60,6 +62,7 @@ varying lowp float halo_width;
uniform lowp float u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
uniform lowp float a_halo_blur_t;
attribute lowp vec2 a_halo_blur;
@@ -68,150 +71,125 @@ varying lowp float halo_blur;
uniform lowp float u_halo_blur;
#endif
-// matrix is for the vertex position.
+
uniform mat4 u_matrix;
+uniform mat4 u_label_plane_matrix;
+uniform mat4 u_gl_coord_matrix;
uniform bool u_is_text;
-uniform mediump float u_zoom;
-uniform bool u_rotate_with_map;
uniform bool u_pitch_with_map;
-uniform mediump float u_pitch;
-uniform mediump float u_bearing;
-uniform mediump float u_aspect_ratio;
-uniform vec2 u_extrude_scale;
+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 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() {
-
+
#ifndef HAS_UNIFORM_u_fill_color
fill_color = unpack_mix_vec4(a_fill_color, a_fill_color_t);
#else
highp vec4 fill_color = u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
halo_color = unpack_mix_vec4(a_halo_color, a_halo_color_t);
#else
highp vec4 halo_color = u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_vec2(a_opacity, a_opacity_t);
#else
lowp float opacity = u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
halo_width = unpack_mix_vec2(a_halo_width, a_halo_width_t);
#else
lowp float halo_width = u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
halo_blur = unpack_mix_vec2(a_halo_blur, a_halo_blur_t);
#else
lowp float halo_blur = u_halo_blur;
#endif
+
vec2 a_pos = a_pos_offset.xy;
vec2 a_offset = a_pos_offset.zw;
vec2 a_tex = a_data.xy;
+ vec2 a_size = a_data.zw;
- mediump vec2 label_data = unpack_float(a_data[2]);
- mediump float a_labelminzoom = label_data[0];
- mediump float a_labelangle = label_data[1];
-
- mediump vec2 a_zoom = unpack_float(a_data[3]);
- mediump float a_minzoom = a_zoom[0];
- mediump float a_maxzoom = a_zoom[1];
+ highp float segment_angle = -a_projected_pos[2];
float size;
- // In order to accommodate placing labels around corners in
- // symbol-placement: line, each glyph in a label could have multiple
- // "quad"s only one of which should be shown at a given zoom level.
- // The min/max zoom assigned to each quad is based on the font size at
- // the vector tile's zoom level, which might be different than at the
- // currently rendered zoom level if text-size is zoom-dependent.
- // Thus, we compensate for this difference by calculating an adjustment
- // based on the scale of rendered text size relative to layout text size.
- mediump float layoutSize;
if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = mix(a_size[0], a_size[1], u_size_t) / 10.0;
- layoutSize = a_size[2] / 10.0;
} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {
size = a_size[0] / 10.0;
- layoutSize = size;
} else if (!u_is_size_zoom_constant && u_is_size_feature_constant) {
size = u_size;
- layoutSize = u_layout_size;
} else {
size = u_size;
- layoutSize = u_size;
}
+ vec4 projectedPoint = u_matrix * vec4(a_pos, 0, 1);
+ highp float camera_to_anchor_distance = projectedPoint.w;
+ // If the label is pitched with the map, layout is done in pitched space,
+ // which makes labels in the distance smaller relative to viewport space.
+ // We counteract part of that effect by multiplying by the perspective ratio.
+ // If the label isn't pitched with the map, we do layout in viewport space,
+ // which makes labels in the distance larger relative to the features around
+ // them. We counteract part of that effect by dividing by the perspective ratio.
+ 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;
+
+ size *= perspective_ratio;
+
float fontScale = u_is_text ? size / 24.0 : size;
- mediump float zoomAdjust = log2(size / layoutSize);
- mediump float adjustedZoom = (u_zoom - zoomAdjust) * 10.0;
- // result: z = 0 if a_minzoom <= adjustedZoom < a_maxzoom, and 1 otherwise
- // Used below to move the vertex out of the clip space for when the current
- // zoom is out of the glyph's zoom range.
- mediump float z = 2.0 - step(a_minzoom, adjustedZoom) - (1.0 - step(a_maxzoom, adjustedZoom));
-
- // pitch-alignment: map
- // rotation-alignment: map | viewport
- if (u_pitch_with_map) {
- lowp float angle = u_rotate_with_map ? (a_labelangle / 256.0 * 2.0 * PI) : u_bearing;
- lowp float asin = sin(angle);
- lowp float acos = cos(angle);
- mat2 RotationMatrix = mat2(acos, asin, -1.0 * asin, acos);
- vec2 offset = RotationMatrix * a_offset;
- vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0);
- gl_Position = u_matrix * vec4(a_pos + extrude, 0, 1);
- gl_Position.z += z * gl_Position.w;
- // pitch-alignment: viewport
- // rotation-alignment: map
- } else if (u_rotate_with_map) {
- // foreshortening factor to apply on pitched maps
- // as a label goes from horizontal <=> vertical in angle
- // it goes from 0% foreshortening to up to around 70% foreshortening
- lowp float pitchfactor = 1.0 - cos(u_pitch * sin(u_pitch * 0.75));
-
- lowp float lineangle = a_labelangle / 256.0 * 2.0 * PI;
-
- // use the lineangle to position points a,b along the line
- // project the points and calculate the label angle in projected space
- // this calculation allows labels to be rendered unskewed on pitched maps
- vec4 a = u_matrix * vec4(a_pos, 0, 1);
- vec4 b = u_matrix * vec4(a_pos + vec2(cos(lineangle),sin(lineangle)), 0, 1);
- lowp float angle = atan((b[1]/b[3] - a[1]/a[3])/u_aspect_ratio, b[0]/b[3] - a[0]/a[3]);
- lowp float asin = sin(angle);
- lowp float acos = cos(angle);
- mat2 RotationMatrix = mat2(acos, -1.0 * asin, asin, acos);
-
- vec2 offset = RotationMatrix * (vec2((1.0-pitchfactor)+(pitchfactor*cos(angle*2.0)), 1.0) * a_offset);
- vec2 extrude = fontScale * u_extrude_scale * (offset / 64.0);
- gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0);
- gl_Position.z += z * gl_Position.w;
- // pitch-alignment: viewport
- // rotation-alignment: viewport
- } else {
- vec2 extrude = fontScale * u_extrude_scale * (a_offset / 64.0);
- gl_Position = u_matrix * vec4(a_pos, 0, 1) + vec4(extrude, 0, 0);
+ highp float symbol_rotation = 0.0;
+ if (u_rotate_symbol) {
+ // Point labels with 'rotation-alignment: map' are horizontal with respect to tile units
+ // To figure out that angle in projected space, we draw a short horizontal line in tile
+ // space, project it, and measure its angle in projected space.
+ vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1);
+
+ vec2 a = projectedPoint.xy / projectedPoint.w;
+ vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w;
+
+ symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x);
}
+ highp float angle_sin = sin(segment_angle + symbol_rotation);
+ highp float angle_cos = cos(segment_angle + symbol_rotation);
+ 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);
float gamma_scale = gl_Position.w;
vec2 tex = a_tex / u_texsize;
- vec2 fade_tex = vec2(a_labelminzoom / 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;
+ float interpolated_fade_opacity = max(0.0, min(1.0, fade_opacity[0] + fade_change));
- v_data0 = vec4(tex.x, tex.y, fade_tex.x, fade_tex.y);
- v_data1 = vec2(gamma_scale, size);
+ v_data0 = vec2(tex.x, tex.y);
+ v_data1 = vec3(gamma_scale, size, interpolated_fade_opacity);
}
)MBGL_SHADER";
@@ -227,64 +205,73 @@ varying highp vec4 fill_color;
uniform highp vec4 u_fill_color;
#endif
+
#ifndef HAS_UNIFORM_u_halo_color
varying highp vec4 halo_color;
#else
uniform highp vec4 u_halo_color;
#endif
+
#ifndef HAS_UNIFORM_u_opacity
varying lowp float opacity;
#else
uniform lowp float u_opacity;
#endif
+
#ifndef HAS_UNIFORM_u_halo_width
varying lowp float halo_width;
#else
uniform lowp float u_halo_width;
#endif
+
#ifndef HAS_UNIFORM_u_halo_blur
varying lowp float halo_blur;
#else
uniform lowp float u_halo_blur;
#endif
+
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() {
-
+
#ifdef HAS_UNIFORM_u_fill_color
highp vec4 fill_color = u_fill_color;
#endif
+
#ifdef HAS_UNIFORM_u_halo_color
highp vec4 halo_color = u_halo_color;
#endif
+
#ifdef HAS_UNIFORM_u_opacity
lowp float opacity = u_opacity;
#endif
+
#ifdef HAS_UNIFORM_u_halo_width
lowp float halo_width = u_halo_width;
#endif
+
#ifdef HAS_UNIFORM_u_halo_blur
lowp float halo_blur = u_halo_blur;
#endif
+
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;
@@ -298,11 +285,10 @@ void main() {
}
lowp float dist = texture2D(u_texture, tex).a;
- lowp float fade_alpha = texture2D(u_fadetexture, fade_tex).a;
highp float gamma_scaled = gamma * gamma_scale;
- highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist) * fade_alpha;
+ highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist);
- gl_FragColor = color * (alpha * opacity);
+ gl_FragColor = color * (alpha * opacity * fade_opacity);
#ifdef OVERDRAW_INSPECTOR
gl_FragColor = vec4(1.0);
diff --git a/src/mbgl/sprite/sprite_loader.cpp b/src/mbgl/sprite/sprite_loader.cpp
index 60ece5ed73..93d6dfd9ae 100644
--- a/src/mbgl/sprite/sprite_loader.cpp
+++ b/src/mbgl/sprite/sprite_loader.cpp
@@ -10,8 +10,8 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
-#include <mbgl/util/run_loop.hpp>
#include <mbgl/actor/actor.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <cassert>
@@ -21,7 +21,7 @@ static SpriteLoaderObserver nullObserver;
struct SpriteLoader::Loader {
Loader(Scheduler& scheduler, SpriteLoader& imageManager)
- : mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ : mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(scheduler, ActorRef<SpriteLoader>(imageManager, mailbox)) {
}
diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp
index 94bba7f8bf..207dd2ee69 100644
--- a/src/mbgl/storage/resource.cpp
+++ b/src/mbgl/storage/resource.cpp
@@ -61,17 +61,19 @@ Resource Resource::image(const std::string& url) {
}
Resource Resource::spriteImage(const std::string& base, float pixelRatio) {
- return Resource {
- Resource::Kind::SpriteImage,
- base + (pixelRatio > 1 ? "@2x" : "") + ".png"
- };
+ util::URL url(base);
+ return Resource{ Resource::Kind::SpriteImage,
+ base.substr(0, url.path.first + url.path.second) +
+ (pixelRatio > 1 ? "@2x" : "") + ".png" +
+ base.substr(url.query.first, url.query.second) };
}
Resource Resource::spriteJSON(const std::string& base, float pixelRatio) {
- return Resource {
- Resource::Kind::SpriteJSON,
- base + (pixelRatio > 1 ? "@2x" : "") + ".json"
- };
+ util::URL url(base);
+ return Resource{ Resource::Kind::SpriteJSON,
+ base.substr(0, url.path.first + url.path.second) +
+ (pixelRatio > 1 ? "@2x" : "") + ".json" +
+ base.substr(url.query.first, url.query.second) };
}
Resource Resource::glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair<uint16_t, uint16_t>& glyphRange) {
@@ -95,7 +97,7 @@ Resource Resource::tile(const std::string& urlTemplate,
int32_t y,
int8_t z,
Tileset::Scheme scheme,
- Necessity necessity) {
+ LoadingMethod loadingMethod) {
bool supportsRatio = urlTemplate.find("{ratio}") != std::string::npos;
if (scheme == Tileset::Scheme::TMS) {
y = (1 << z) - y - 1;
@@ -131,7 +133,7 @@ Resource Resource::tile(const std::string& urlTemplate,
y,
z
},
- necessity
+ loadingMethod
};
}
diff --git a/src/mbgl/storage/response.cpp b/src/mbgl/storage/response.cpp
index a174339074..222f55db84 100644
--- a/src/mbgl/storage/response.cpp
+++ b/src/mbgl/storage/response.cpp
@@ -14,6 +14,7 @@ Response& Response::operator=(const Response& res) {
error = res.error ? std::make_unique<Error>(*res.error) : nullptr;
noContent = res.noContent;
notModified = res.notModified;
+ mustRevalidate = res.mustRevalidate;
data = res.data;
modified = res.modified;
expires = res.expires;
diff --git a/src/mbgl/style/conversion/constant.cpp b/src/mbgl/style/conversion/constant.cpp
new file mode 100644
index 0000000000..e837c4e70b
--- /dev/null
+++ b/src/mbgl/style/conversion/constant.cpp
@@ -0,0 +1,94 @@
+#include <mbgl/style/conversion/constant.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<bool> Converter<bool>::operator()(const Convertible& value, Error& error) const {
+ optional<bool> converted = toBool(value);
+ if (!converted) {
+ error = { "value must be a boolean" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<float> Converter<float>::operator()(const Convertible& value, Error& error) const {
+ optional<float> converted = toNumber(value);
+ if (!converted) {
+ error = { "value must be a number" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<std::string> Converter<std::string>::operator()(const Convertible& value, Error& error) const {
+ optional<std::string> converted = toString(value);
+ if (!converted) {
+ error = { "value must be a string" };
+ return {};
+ }
+ return *converted;
+}
+
+optional<Color> Converter<Color>::operator()(const Convertible& value, Error& error) const {
+ optional<std::string> string = toString(value);
+ if (!string) {
+ error = { "value must be a string" };
+ return {};
+ }
+
+ optional<Color> color = Color::parse(*string);
+ if (!color) {
+ error = { "value must be a valid color" };
+ return {};
+ }
+
+ return *color;
+}
+
+optional<std::vector<float>> Converter<std::vector<float>>::operator()(const Convertible& value, Error& error) const {
+ if (!isArray(value)) {
+ error = { "value must be an array" };
+ return {};
+ }
+
+ std::vector<float> result;
+ result.reserve(arrayLength(value));
+
+ for (std::size_t i = 0; i < arrayLength(value); ++i) {
+ optional<float> number = toNumber(arrayMember(value, i));
+ if (!number) {
+ error = { "value must be an array of numbers" };
+ return {};
+ }
+ result.push_back(*number);
+ }
+
+ return result;
+}
+
+optional<std::vector<std::string>> Converter<std::vector<std::string>>::operator()(const Convertible& value, Error& error) const {
+ if (!isArray(value)) {
+ error = { "value must be an array" };
+ return {};
+ }
+
+ std::vector<std::string> result;
+ result.reserve(arrayLength(value));
+
+ for (std::size_t i = 0; i < arrayLength(value); ++i) {
+ optional<std::string> string = toString(arrayMember(value, i));
+ if (!string) {
+ error = { "value must be an array of strings" };
+ return {};
+ }
+ result.push_back(*string);
+ }
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/coordinate.cpp b/src/mbgl/style/conversion/coordinate.cpp
new file mode 100644
index 0000000000..9b2be3381e
--- /dev/null
+++ b/src/mbgl/style/conversion/coordinate.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/style/conversion/coordinate.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<LatLng> Converter<LatLng>::operator() (const Convertible& value, Error& error) const {
+ if (!isArray(value) || arrayLength(value) < 2 ) {
+ error = { "coordinate array must contain numeric longitude and latitude values" };
+ return {};
+ }
+ //Style spec uses GeoJSON convention for specifying coordinates
+ optional<double> latitude = toDouble(arrayMember(value, 1));
+ optional<double> longitude = toDouble(arrayMember(value, 0));
+
+ if (!latitude || !longitude) {
+ error = { "coordinate array must contain numeric longitude and latitude values" };
+ return {};
+ }
+ if (*latitude < -90 || *latitude > 90 ){
+ error = { "coordinate latitude must be between -90 and 90" };
+ return {};
+ }
+ return LatLng(*latitude, *longitude);
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp
new file mode 100644
index 0000000000..bb7bb6ea98
--- /dev/null
+++ b/src/mbgl/style/conversion/filter.cpp
@@ -0,0 +1,248 @@
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/util/geometry.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+static optional<Value> normalizeValue(const optional<Value>& value, Error& error) {
+ if (!value) {
+ error = { "filter expression value must be a boolean, number, or string" };
+ return {};
+ } else {
+ return *value;
+ }
+}
+
+static optional<FeatureType> toFeatureType(const Convertible& value, Error& error) {
+ optional<std::string> type = toString(value);
+ if (!type) {
+ error = { "value for $type filter must be a string" };
+ return {};
+ } else if (*type == "Point") {
+ return FeatureType::Point;
+ } else if (*type == "LineString") {
+ return FeatureType::LineString;
+ } else if (*type == "Polygon") {
+ return FeatureType::Polygon;
+ } else {
+ error = { "value for $type filter must be Point, LineString, or Polygon" };
+ return {};
+ }
+}
+
+static optional<FeatureIdentifier> toFeatureIdentifier(const Convertible& value, Error& error) {
+ optional<Value> identifier = toValue(value);
+ if (!identifier) {
+ error = { "filter expression value must be a boolean, number, or string" };
+ return {};
+ } else {
+ return (*identifier).match(
+ [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
+ [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
+ [] ( double t) -> optional<FeatureIdentifier> { return { t }; },
+ [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
+ [&] (const auto&) -> optional<FeatureIdentifier> {
+ error = { "filter expression value must be a boolean, number, or string" };
+ return {};
+ });
+ }
+}
+
+template <class FilterType, class IdentifierFilterType>
+optional<Filter> convertUnaryFilter(const Convertible& value, Error& error) {
+ if (arrayLength(value) < 2) {
+ error = { "filter expression must have 2 elements" };
+ return {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key must be a string" };
+ return {};
+ }
+
+ if (*key == "$id") {
+ return { IdentifierFilterType {} };
+ } else {
+ return { FilterType { *key } };
+ }
+}
+
+template <class FilterType, class TypeFilterType, class IdentifierFilterType>
+optional<Filter> convertEqualityFilter(const Convertible& value, Error& error) {
+ if (arrayLength(value) < 3) {
+ error = { "filter expression must have 3 elements" };
+ return {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key must be a string" };
+ return {};
+ }
+
+ if (*key == "$type") {
+ optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { TypeFilterType { *filterValue } };
+
+ } else if (*key == "$id") {
+ optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { IdentifierFilterType { *filterValue } };
+
+ } else {
+ optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { FilterType { *key, *filterValue } };
+ }
+}
+
+template <class FilterType>
+optional<Filter> convertBinaryFilter(const Convertible& value, Error& error) {
+ if (arrayLength(value) < 3) {
+ error = { "filter expression must have 3 elements" };
+ return {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key must be a string" };
+ return {};
+ }
+
+ optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
+ if (!filterValue) {
+ return {};
+ }
+
+ return { FilterType { *key, *filterValue } };
+}
+
+template <class FilterType, class TypeFilterType, class IdentifierFilterType>
+optional<Filter> convertSetFilter(const Convertible& value, Error& error) {
+ if (arrayLength(value) < 2) {
+ error = { "filter expression must at least 2 elements" };
+ return {};
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ error = { "filter expression key must be a string" };
+ return {};
+ }
+
+ if (*key == "$type") {
+ std::vector<FeatureType> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
+ if (!filterValue) {
+ return {};
+ }
+ values.push_back(*filterValue);
+ }
+
+ return { TypeFilterType { std::move(values) } };
+
+ } else if (*key == "$id") {
+ std::vector<FeatureIdentifier> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
+ if (!filterValue) {
+ return {};
+ }
+ values.push_back(*filterValue);
+ }
+
+ return { IdentifierFilterType { std::move(values) } };
+
+ } else {
+ std::vector<Value> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
+ if (!filterValue) {
+ return {};
+ }
+ values.push_back(*filterValue);
+ }
+
+ return { FilterType { *key, std::move(values) } };
+ }
+}
+
+template <class FilterType>
+optional<Filter> convertCompoundFilter(const Convertible& value, Error& error) {
+ std::vector<Filter> filters;
+ for (std::size_t i = 1; i < arrayLength(value); ++i) {
+ optional<Filter> element = convert<Filter>(arrayMember(value, i), error);
+ if (!element) {
+ return {};
+ }
+ filters.push_back(*element);
+ }
+
+ return { FilterType { std::move(filters) } };
+}
+
+optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
+ if (!isArray(value)) {
+ error = { "filter expression must be an array" };
+ return {};
+ }
+
+ if (arrayLength(value) < 1) {
+ error = { "filter expression must have at least 1 element" };
+ return {};
+ }
+
+ optional<std::string> op = toString(arrayMember(value, 0));
+ if (!op) {
+ error = { "filter operator must be a string" };
+ return {};
+ }
+
+ if (*op == "==") {
+ return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
+ } else if (*op == "!=") {
+ return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
+ } else if (*op == ">") {
+ return convertBinaryFilter<GreaterThanFilter>(value, error);
+ } else if (*op == ">=") {
+ return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
+ } else if (*op == "<") {
+ return convertBinaryFilter<LessThanFilter>(value, error);
+ } else if (*op == "<=") {
+ return convertBinaryFilter<LessThanEqualsFilter>(value, error);
+ } else if (*op == "in") {
+ return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
+ } else if (*op == "!in") {
+ return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
+ } else if (*op == "all") {
+ return convertCompoundFilter<AllFilter>(value, error);
+ } else if (*op == "any") {
+ return convertCompoundFilter<AnyFilter>(value, error);
+ } else if (*op == "none") {
+ return convertCompoundFilter<NoneFilter>(value, error);
+ } else if (*op == "has") {
+ return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
+ } else if (*op == "!has") {
+ return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
+ }
+
+ error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
+ return {};
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/geojson.cpp b/src/mbgl/style/conversion/geojson.cpp
index 8103e9014a..e39a1a80eb 100644
--- a/src/mbgl/style/conversion/geojson.cpp
+++ b/src/mbgl/style/conversion/geojson.cpp
@@ -1,26 +1,16 @@
#include <mbgl/style/conversion/geojson.hpp>
#include <mbgl/style/conversion/json.hpp>
-#include <mbgl/util/rapidjson.hpp>
-
-#include <mapbox/geojson.hpp>
-#include <mapbox/geojson/rapidjson.hpp>
namespace mbgl {
namespace style {
namespace conversion {
-optional<GeoJSON> Converter<GeoJSON>::operator()(const std::string& value, Error& error) const {
- return convertJSON<GeoJSON>(value, error);
+optional<GeoJSON> Converter<GeoJSON>::operator()(const Convertible& value, Error& error) const {
+ return toGeoJSON(value, error);
}
-template <>
-optional<GeoJSON> Converter<GeoJSON>::operator()(const JSValue& value, Error& error) const {
- try {
- return mapbox::geojson::convert(value);
- } catch (const std::exception& ex) {
- error = { ex.what() };
- return {};
- }
+optional<GeoJSON> parseGeoJSON(const std::string& value, Error& error) {
+ return convertJSON<GeoJSON>(value, error);
}
} // namespace conversion
diff --git a/src/mbgl/style/conversion/geojson_options.cpp b/src/mbgl/style/conversion/geojson_options.cpp
new file mode 100644
index 0000000000..a2c5ed8816
--- /dev/null
+++ b/src/mbgl/style/conversion/geojson_options.cpp
@@ -0,0 +1,85 @@
+#include <mbgl/style/conversion/geojson_options.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible& value, Error& error) const {
+ GeoJSONOptions options;
+
+ const auto minzoomValue = objectMember(value, "minzoom");
+ if (minzoomValue) {
+ if (toNumber(*minzoomValue)) {
+ options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue));
+ } else {
+ error = { "GeoJSON source minzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto maxzoomValue = objectMember(value, "maxzoom");
+ if (maxzoomValue) {
+ if (toNumber(*maxzoomValue)) {
+ options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue));
+ } else {
+ error = { "GeoJSON source maxzoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto bufferValue = objectMember(value, "buffer");
+ if (bufferValue) {
+ if (toNumber(*bufferValue)) {
+ options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
+ } else {
+ error = { "GeoJSON source buffer value must be a number" };
+ return {};
+ }
+ }
+
+ const auto toleranceValue = objectMember(value, "tolerance");
+ if (toleranceValue) {
+ if (toNumber(*toleranceValue)) {
+ options.tolerance = static_cast<double>(*toNumber(*toleranceValue));
+ } else {
+ error = { "GeoJSON source tolerance value must be a number" };
+ return {};
+ }
+ }
+
+ const auto clusterValue = objectMember(value, "cluster");
+ if (clusterValue) {
+ if (toBool(*clusterValue)) {
+ options.cluster = *toBool(*clusterValue);
+ } else {
+ error = { "GeoJSON source cluster value must be a boolean" };
+ return {};
+ }
+ }
+
+ const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom");
+ if (clusterMaxZoomValue) {
+ if (toNumber(*clusterMaxZoomValue)) {
+ options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue));
+ } else {
+ error = { "GeoJSON source clusterMaxZoom value must be a number" };
+ return {};
+ }
+ }
+
+ const auto clusterRadiusValue = objectMember(value, "clusterRadius");
+ if (clusterRadiusValue) {
+ if (toNumber(*clusterRadiusValue)) {
+ options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue));
+ } else {
+ error = { "GeoJSON source clusterRadius value must be a number" };
+ return {};
+ }
+ }
+
+ return { options };
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/get_json_type.cpp b/src/mbgl/style/conversion/get_json_type.cpp
new file mode 100644
index 0000000000..cd3b4608b1
--- /dev/null
+++ b/src/mbgl/style/conversion/get_json_type.cpp
@@ -0,0 +1,34 @@
+#include <mbgl/style/conversion/get_json_type.hpp>
+#include <mbgl/util/feature.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+std::string getJSONType(const Convertible& value) {
+ if (isUndefined(value)) {
+ return "null";
+ }
+ if (isArray(value)) {
+ return "array";
+ }
+ if (isObject(value)) {
+ return "object";
+ }
+ optional<mbgl::Value> v = toValue(value);
+
+ // Since we've already checked the non-atomic types above, value must then
+ // be a string, number, or boolean -- thus, assume that the toValue()
+ // conversion succeeds.
+ assert(v);
+
+ return v->match(
+ [&] (const std::string&) { return "string"; },
+ [&] (bool) { return "boolean"; },
+ [&] (auto) { return "number"; }
+ );
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/json.hpp b/src/mbgl/style/conversion/json.hpp
index 0817ac09df..7dd2378f6b 100644
--- a/src/mbgl/style/conversion/json.hpp
+++ b/src/mbgl/style/conversion/json.hpp
@@ -20,7 +20,7 @@ optional<T> convertJSON(const std::string& json, Error& error, Args&&...args) {
return {};
}
- return convert<T, JSValue>(document, error, std::forward<Args>(args)...);
+ return convert<T>(document, error, std::forward<Args>(args)...);
}
} // namespace conversion
diff --git a/src/mbgl/style/conversion/layer.cpp b/src/mbgl/style/conversion/layer.cpp
new file mode 100644
index 0000000000..0ca582f8dc
--- /dev/null
+++ b/src/mbgl/style/conversion/layer.cpp
@@ -0,0 +1,206 @@
+#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/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 {};
+ }
+ 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>> 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 == "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..074d7eb730
--- /dev/null
+++ b/src/mbgl/style/conversion/make_property_setters.hpp
@@ -0,0 +1,209 @@
+#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 {
+
+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, PropertyValue<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["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["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..9e382b9c38 100644
--- a/include/mbgl/style/conversion/property_setter.hpp
+++ b/src/mbgl/style/conversion/property_setter.hpp
@@ -13,11 +13,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 +32,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 +49,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..c10d0babcf
--- /dev/null
+++ b/src/mbgl/style/conversion/source.cpp
@@ -0,0 +1,175 @@
+#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/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>> 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 {};
+ }
+
+ 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 {};
+ }
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp
new file mode 100644
index 0000000000..b9383c41b8
--- /dev/null
+++ b/src/mbgl/style/conversion/tileset.cpp
@@ -0,0 +1,73 @@
+#include <mbgl/style/conversion/tileset.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error& error) const {
+ Tileset result;
+
+ auto tiles = objectMember(value, "tiles");
+ if (!tiles) {
+ error = { "source must have tiles" };
+ return {};
+ }
+
+ if (!isArray(*tiles)) {
+ error = { "source tiles must be an array" };
+ return {};
+ }
+
+ for (std::size_t i = 0; i < arrayLength(*tiles); i++) {
+ optional<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);
+ }
+
+ return result;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl
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..76248b84bd
--- /dev/null
+++ b/src/mbgl/style/custom_tile_loader.cpp
@@ -0,0 +1,109 @@
+#include <mbgl/style/custom_tile_loader.hpp>
+#include <mbgl/tile/custom_geometry_tile.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> ) {
+ for (auto idtuple= tileCallbackMap.begin(); idtuple != tileCallbackMap.end(); idtuple++) {
+ const LatLngBounds tileBounds(idtuple->first);
+ if (tileBounds.intersects(bounds, LatLng::Wrapped) || bounds.contains(tileBounds, LatLng::Wrapped) || tileBounds.contains(bounds, LatLng::Wrapped)) {
+ 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..29f6a47b10
--- /dev/null
+++ b/src/mbgl/style/expression/array_assertion.cpp
@@ -0,0 +1,86 @@
+#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)
+ ));
+}
+
+} // 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..a17c53cf54
--- /dev/null
+++ b/src/mbgl/style/expression/assertion.cpp
@@ -0,0 +1,73 @@
+#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)));
+}
+
+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;
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
+
+
diff --git a/src/mbgl/style/expression/at.cpp b/src/mbgl/style/expression/at.cpp
new file mode 100644
index 0000000000..e447d33bc7
--- /dev/null
+++ b/src/mbgl/style/expression/at.cpp
@@ -0,0 +1,64 @@
+#include <mbgl/style/expression/at.hpp>
+#include <mbgl/util/string.hpp>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+EvaluationResult At::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult evaluatedIndex = index->evaluate(params);
+ const EvaluationResult evaluatedInput = input->evaluate(params);
+ if (!evaluatedIndex) {
+ return evaluatedIndex.error();
+ }
+ if (!evaluatedInput) {
+ return evaluatedInput.error();
+ }
+
+ const auto i = evaluatedIndex->get<double>();
+ const auto inputArray = evaluatedInput->get<std::vector<Value>>();
+
+ if (i < 0 || i >= inputArray.size()) {
+ return EvaluationError {
+ "Array index out of bounds: " + stringify(i) +
+ " > " + util::toString(inputArray.size()) + "."
+ };
+ }
+ if (i != std::floor(i)) {
+ return EvaluationError {
+ "Array index must be an integer, but found " + stringify(i) + " instead."
+ };
+ }
+ return inputArray[static_cast<std::size_t>(i)];
+}
+
+void At::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*index);
+ visit(*input);
+}
+
+using namespace mbgl::style::conversion;
+ParseResult At::parse(const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value));
+
+ std::size_t length = arrayLength(value);
+ if (length != 3) {
+ ctx.error("Expected 2 arguments, but found " + util::toString(length - 1) + " instead.");
+ return ParseResult();
+ }
+
+ ParseResult index = ctx.parse(arrayMember(value, 1), 1, {type::Number});
+
+ type::Type inputType = type::Array(ctx.getExpected() ? *ctx.getExpected() : type::Value);
+ ParseResult input = ctx.parse(arrayMember(value, 2), 2, {inputType});
+
+ if (!index || !input) return ParseResult();
+
+ return ParseResult(std::make_unique<At>(std::move(*index), std::move(*input)));
+
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/boolean_operator.cpp b/src/mbgl/style/expression/boolean_operator.cpp
new file mode 100644
index 0000000000..88797f965a
--- /dev/null
+++ b/src/mbgl/style/expression/boolean_operator.cpp
@@ -0,0 +1,87 @@
+#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;
+}
+
+
+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;
+}
+
+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..049f258606
--- /dev/null
+++ b/src/mbgl/style/expression/case.cpp
@@ -0,0 +1,91 @@
+#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;
+}
+
+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..bfde3c7581
--- /dev/null
+++ b/src/mbgl/style/expression/coalesce.cpp
@@ -0,0 +1,62 @@
+#include <mbgl/style/expression/coalesce.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;
+}
+
+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;
+ if (ctx.getExpected() && *ctx.getExpected() != type::Value) {
+ outputType = ctx.getExpected();
+ }
+
+ 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);
+ if (!parsed) {
+ return parsed;
+ }
+ if (!outputType) {
+ outputType = (*parsed)->getType();
+ }
+ args.push_back(std::move(*parsed));
+ }
+
+ assert(outputType);
+ return ParseResult(std::make_unique<Coalesce>(*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..8ed8e160dd
--- /dev/null
+++ b/src/mbgl/style/expression/coercion.cpp
@@ -0,0 +1,144 @@
+#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);
+ }
+}
+
+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;
+}
+
+} // 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..70bc6dfd95
--- /dev/null
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -0,0 +1,568 @@
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/check_subtype.hpp>
+#include <mbgl/style/expression/util.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/math/log2.hpp>
+#include <mbgl/util/ignore.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/platform.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+namespace detail {
+
+/*
+ The Signature<Fn> structs are wrappers around an "evaluate()" function whose
+ purpose is to extract the necessary Type data from the evaluate function's
+ type. There are three key (partial) specializations:
+
+ Signature<R (Params...)>:
+ Wraps a simple evaluate function (const T0&, const T1&, ...) -> Result<U>
+
+ Signature<R (const Varargs<T>&)>:
+ Wraps an evaluate function that takes an arbitrary number of arguments (via
+ a Varargs<T>, which is just an alias for std::vector).
+
+ Signature<R (const EvaluationContext&, Params...)>:
+ Wraps an evaluate function that needs to access the expression evaluation
+ parameters in addition to its subexpressions, i.e.,
+ (const EvaluationParams& const T0&, const T1&, ...) -> Result<U>. Needed
+ for expressions like ["zoom"], ["get", key], etc.
+
+ In each of the above evaluate signatures, T0, T1, etc. are the types of
+ the successfully evaluated subexpressions.
+*/
+template <class, class Enable = void>
+struct Signature;
+
+// Simple evaluate function (const T0&, const T1&, ...) -> Result<U>
+template <class R, class... Params>
+struct Signature<R (Params...)> : SignatureBase {
+ using Args = std::array<std::unique_ptr<Expression>, sizeof...(Params)>;
+
+ Signature(R (*evaluate_)(Params...)) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ std::vector<type::Type> {valueTypeToExpressionType<std::decay_t<Params>>()...}
+ ),
+ 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(const std::string& name,
+ 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>&)) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ VarargsType { valueTypeToExpressionType<T>() }
+ ),
+ evaluate(evaluate_)
+ {}
+
+ std::unique_ptr<Expression> makeExpression(const std::string& name,
+ 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...)) :
+ SignatureBase(
+ valueTypeToExpressionType<std::decay_t<typename R::Value>>(),
+ std::vector<type::Type> {valueTypeToExpressionType<std::decay_t<Params>>()...}
+ ),
+ evaluate(evaluate_)
+ {}
+
+ std::unique_ptr<Expression> makeExpression(const std::string& name,
+ 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 T>
+Result<bool> equal(const T& lhs, const T& rhs) { return lhs == rhs; }
+
+template <typename T>
+Result<bool> notEqual(const T& lhs, const T& rhs) { return lhs != rhs; }
+
+template <typename Fn>
+static std::unique_ptr<detail::SignatureBase> makeSignature(Fn evaluateFunction) {
+ return std::make_unique<detail::Signature<Fn>>(evaluateFunction);
+}
+
+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));
+ };
+
+ define("e", []() -> Result<double> { return 2.718281828459045; });
+ define("pi", []() -> Result<double> { return 3.141592653589793; });
+ define("ln2", []() -> Result<double> { return 0.6931471805599453; });
+
+ define("typeof", [](const Value& v) -> Result<std::string> { return toString(typeOf(v)); });
+
+ define("to-string", [](const Value& value) -> Result<std::string> {
+ return value.match(
+ [](const Color& c) -> Result<std::string> { return c.stringify(); }, // avoid quoting
+ [](const std::string& s) -> Result<std::string> { return s; }, // avoid quoting
+ [](const auto& v) -> Result<std::string> { return stringify(v); }
+ );
+ });
+
+ define("to-boolean", [](const Value& v) -> Result<bool> {
+ return v.match(
+ [&] (double f) { return (bool)f; },
+ [&] (const std::string& s) { return s.length() > 0; },
+ [&] (bool b) { return b; },
+ [&] (const NullValue&) { return false; },
+ [&] (const auto&) { return true; }
+ );
+ });
+ define("to-rgba", [](const Color& color) -> Result<std::array<double, 4>> {
+ return std::array<double, 4> {{ color.r, color.g, color.b, color.a }};
+ });
+
+ define("rgba", rgba);
+ define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); });
+
+ define("zoom", [](const EvaluationContext& params) -> Result<double> {
+ if (!params.zoom) {
+ return EvaluationError {
+ "The 'zoom' expression is unavailable in the current evaluation context."
+ };
+ }
+ return *(params.zoom);
+ });
+
+ define("heatmap-density", [](const EvaluationContext& params) -> Result<double> {
+ if (!params.heatmapDensity) {
+ return EvaluationError {
+ "The 'heatmap-density' expression is unavailable in the current evaluation context."
+ };
+ }
+ return *(params.heatmapDensity);
+ });
+
+ define("has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ return params.feature->getValue(key) ? true : false;
+ });
+ define("has", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<bool> {
+ return object.find(key) != object.end();
+ });
+
+ define("get", [](const EvaluationContext& params, const std::string& key) -> Result<Value> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ auto propertyValue = params.feature->getValue(key);
+ if (!propertyValue) {
+ return Null;
+ }
+ return Value(toExpressionValue(*propertyValue));
+ });
+ define("get", [](const std::string& key, const std::unordered_map<std::string, Value>& object) -> Result<Value> {
+ if (object.find(key) == object.end()) {
+ return Null;
+ }
+ return object.at(key);
+ });
+
+ define("length", [](const std::vector<Value>& arr) -> Result<double> {
+ return arr.size();
+ });
+ define("length", [] (const std::string s) -> Result<double> {
+ return s.size();
+ });
+
+ define("properties", [](const EvaluationContext& params) -> Result<std::unordered_map<std::string, Value>> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+ std::unordered_map<std::string, Value> result;
+ const PropertyMap properties = params.feature->getProperties();
+ for (const auto& entry : properties) {
+ result[entry.first] = toExpressionValue(entry.second);
+ }
+ return result;
+ });
+
+ define("geometry-type", [](const EvaluationContext& params) -> Result<std::string> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ auto type = params.feature->getType();
+ if (type == FeatureType::Point) {
+ return "Point";
+ } else if (type == FeatureType::LineString) {
+ return "LineString";
+ } else if (type == FeatureType::Polygon) {
+ return "Polygon";
+ } else {
+ return "Unknown";
+ }
+ });
+
+ define("id", [](const EvaluationContext& params) -> Result<Value> {
+ if (!params.feature) {
+ return EvaluationError {
+ "Feature data is unavailable in the current evaluation context."
+ };
+ }
+
+ auto id = params.feature->getID();
+ if (!id) {
+ return Null;
+ }
+ return id->match(
+ [](const auto& idValue) {
+ return toExpressionValue(mbgl::Value(idValue));
+ }
+ );
+ });
+
+ define("+", [](const Varargs<double>& args) -> Result<double> {
+ double sum = 0.0f;
+ for (auto arg : args) {
+ sum += arg;
+ }
+ return sum;
+ });
+ define("-", [](double a, double b) -> Result<double> { return a - b; });
+ define("-", [](double a) -> Result<double> { return -a; });
+ define("*", [](const Varargs<double>& args) -> Result<double> {
+ double prod = 1.0f;
+ for (auto arg : args) {
+ prod *= arg;
+ }
+ return prod;
+ });
+ define("/", [](double a, double b) -> Result<double> { return a / b; });
+ define("%", [](double a, double b) -> Result<double> { return fmod(a, b); });
+ define("^", [](double a, double b) -> Result<double> { return pow(a, b); });
+ define("sqrt", [](double x) -> Result<double> { return sqrt(x); });
+ define("log10", [](double x) -> Result<double> { return log10(x); });
+ define("ln", [](double x) -> Result<double> { return log(x); });
+ define("log2", [](double x) -> Result<double> { return util::log2(x); });
+ define("sin", [](double x) -> Result<double> { return sin(x); });
+ define("cos", [](double x) -> Result<double> { return cos(x); });
+ define("tan", [](double x) -> Result<double> { return tan(x); });
+ define("asin", [](double x) -> Result<double> { return asin(x); });
+ define("acos", [](double x) -> Result<double> { return acos(x); });
+ define("atan", [](double x) -> Result<double> { return atan(x); });
+
+ define("min", [](const Varargs<double>& args) -> Result<double> {
+ double result = std::numeric_limits<double>::infinity();
+ for (double arg : args) {
+ result = fmin(arg, result);
+ }
+ return result;
+ });
+ define("max", [](const Varargs<double>& args) -> Result<double> {
+ double result = -std::numeric_limits<double>::infinity();
+ for (double arg : args) {
+ result = fmax(arg, result);
+ }
+ return result;
+ });
+
+ define("==", equal<double>);
+ define("==", equal<const std::string&>);
+ define("==", equal<bool>);
+ define("==", equal<NullValue>);
+
+ define("!=", notEqual<double>);
+ define("!=", notEqual<const std::string&>);
+ define("!=", notEqual<bool>);
+ define("!=", notEqual<NullValue>);
+
+ define(">", [](double lhs, double rhs) -> Result<bool> { return lhs > rhs; });
+ define(">", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs > rhs; });
+ define(">=", [](double lhs, double rhs) -> Result<bool> { return lhs >= rhs; });
+ define(">=",[](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs >= rhs; });
+ define("<", [](double lhs, double rhs) -> Result<bool> { return lhs < rhs; });
+ define("<", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs < rhs; });
+ define("<=", [](double lhs, double rhs) -> Result<bool> { return lhs <= rhs; });
+ define("<=", [](const std::string& lhs, const std::string& rhs) -> Result<bool> { return lhs <= rhs; });
+
+ define("!", [](bool e) -> Result<bool> { return !e; });
+
+ define("upcase", [](const std::string& input) -> Result<std::string> {
+ return platform::uppercase(input);
+ });
+ define("downcase", [](const std::string& input) -> Result<std::string> {
+ return platform::lowercase(input);
+ });
+ define("concat", [](const Varargs<std::string>& args) -> Result<std::string> {
+ std::string s;
+ for (const std::string& arg : args) {
+ s += arg;
+ }
+ return s;
+ });
+ define("error", [](const std::string& input) -> Result<type::ErrorType> {
+ return EvaluationError { input };
+ });
+
+ return definitions;
+}
+
+std::unordered_map<std::string, Definition> CompoundExpressionRegistry::definitions = initializeDefinitions();
+
+using namespace mbgl::style::conversion;
+ParseResult parseCompoundExpression(const std::string name, const Convertible& value, ParsingContext& ctx) {
+ assert(isArray(value) && arrayLength(value) > 0);
+
+ auto it = CompoundExpressionRegistry::definitions.find(name);
+ if (it == CompoundExpressionRegistry::definitions.end()) {
+ ctx.error(
+ R"(Unknown expression ")" + name + R"(". If you wanted a literal array, use ["literal", [...]].)",
+ 0
+ );
+ return ParseResult();
+ }
+ const CompoundExpressionRegistry::Definition& definition = it->second;
+
+ auto length = arrayLength(value);
+
+ // Check if we have a single signature with the correct number of
+ // parameters. If so, then use that signature's parameter types for parsing
+ // (and inferring the types of) the arguments.
+ optional<std::size_t> singleMatchingSignature;
+ for (std::size_t j = 0; j < definition.size(); j++) {
+ const std::unique_ptr<detail::SignatureBase>& signature = definition[j];
+ if (
+ signature->params.is<VarargsType>() ||
+ signature->params.get<std::vector<type::Type>>().size() == length - 1
+ ) {
+ if (singleMatchingSignature) {
+ singleMatchingSignature = {};
+ } else {
+ singleMatchingSignature = j;
+ }
+ }
+ }
+
+ // parse subexpressions first
+ std::vector<std::unique_ptr<Expression>> args;
+ args.reserve(length - 1);
+ for (std::size_t i = 1; i < length; i++) {
+ optional<type::Type> expected;
+
+ if (singleMatchingSignature) {
+ expected = definition[*singleMatchingSignature]->params.match(
+ [](const VarargsType& varargs) { return varargs.type; },
+ [&](const std::vector<type::Type>& params_) { return params_[i - 1]; }
+ );
+ }
+
+ auto parsed = ctx.parse(arrayMember(value, i), i, expected);
+ if (!parsed) {
+ return parsed;
+ }
+ args.push_back(std::move(*parsed));
+ }
+ return createCompoundExpression(name, definition, std::move(args), ctx);
+}
+
+
+ParseResult createCompoundExpression(const std::string& name,
+ std::vector<std::unique_ptr<Expression>> args,
+ ParsingContext& ctx)
+{
+ return createCompoundExpression(name, CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx);
+}
+
+
+ParseResult createCompoundExpression(const std::string& name,
+ 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(name, 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/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..5ddfca8e9f
--- /dev/null
+++ b/src/mbgl/style/expression/interpolate.cpp
@@ -0,0 +1,211 @@
+#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();
+ }
+ );
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/is_constant.cpp b/src/mbgl/style/expression/is_constant.cpp
new file mode 100644
index 0000000000..0ebb37faa9
--- /dev/null
+++ b/src/mbgl/style/expression/is_constant.cpp
@@ -0,0 +1,40 @@
+#include <mbgl/style/expression/is_constant.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isFeatureConstant(const Expression& expression) {
+ if (auto e = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ const std::string name = e->getName();
+ optional<std::size_t> parameterCount = e->getParameterCount();
+ if (name == "get" && parameterCount && *parameterCount == 1) {
+ return false;
+ } else if (name == "has" && parameterCount && *parameterCount == 1) {
+ return false;
+ } else if (
+ name == "properties" ||
+ name == "geometry-type" ||
+ name == "id"
+ ) {
+ return false;
+ }
+ }
+
+ bool featureConstant = true;
+ expression.eachChild([&](const Expression& e) {
+ if (featureConstant && !isFeatureConstant(e)) {
+ featureConstant = false;
+ }
+ });
+ return featureConstant;
+}
+
+bool isZoomConstant(const Expression& e) {
+ return isGlobalPropertyConstant(e, std::array<std::string, 1>{{"zoom"}});
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/is_expression.cpp b/src/mbgl/style/expression/is_expression.cpp
new file mode 100644
index 0000000000..77212f6a1e
--- /dev/null
+++ b/src/mbgl/style/expression/is_expression.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <mbgl/style/conversion.hpp>
+
+#include <unordered_set>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+using namespace mbgl::style::conversion;
+
+bool isExpression(const Convertible& value) {
+ const ExpressionRegistry& registry = getExpressionRegistry();
+
+ if (!isArray(value) || arrayLength(value) == 0) return false;
+ optional<std::string> name = toString(arrayMember(value, 0));
+ if (!name) return false;
+
+ return (registry.find(*name) != registry.end()) ||
+ (CompoundExpressionRegistry::definitions.find(*name) != CompoundExpressionRegistry::definitions.end());
+}
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/let.cpp b/src/mbgl/style/expression/let.cpp
new file mode 100644
index 0000000000..561e8515d4
--- /dev/null
+++ b/src/mbgl/style/expression/let.cpp
@@ -0,0 +1,92 @@
+#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);
+}
+
+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 std::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_)));
+}
+
+EvaluationResult Var::evaluate(const EvaluationContext& params) const {
+ return value->evaluate(params);
+}
+
+void Var::eachChild(const std::function<void(const Expression&)>&) const {}
+
+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)));
+}
+
+} // 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..7e79fcbfe6
--- /dev/null
+++ b/src/mbgl/style/expression/literal.cpp
@@ -0,0 +1,108 @@
+#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));
+ }
+}
+
+} // 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..35356747c9
--- /dev/null
+++ b/src/mbgl/style/expression/match.cpp
@@ -0,0 +1,263 @@
+#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<> 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..501ba2749f
--- /dev/null
+++ b/src/mbgl/style/expression/parsing_context.cpp
@@ -0,0 +1,208 @@
+
+#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/interpolate.hpp>
+#include <mbgl/style/expression/let.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/step.hpp>
+
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+
+#include <mbgl/style/conversion/get_json_type.hpp>
+
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+bool isConstant(const Expression& expression) {
+ if (dynamic_cast<const Var*>(&expression)) {
+ return false;
+ }
+
+ if (auto compound = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ if (compound->getName() == "error") {
+ return false;
+ }
+ }
+
+ bool literalArgs = true;
+ expression.eachChild([&](const Expression& child) {
+ if (!dynamic_cast<const Literal*>(&child)) {
+ literalArgs = false;
+ }
+ });
+ if (!literalArgs) {
+ return false;
+ }
+
+ return isFeatureConstant(expression) &&
+ isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "heatmap-density"}});
+}
+
+using namespace mbgl::style::conversion;
+
+ParseResult ParsingContext::parse(const Convertible& value, std::size_t index_, optional<type::Type> expected_) {
+ ParsingContext child(key + "[" + util::toString(index_) + "]",
+ errors,
+ std::move(expected_),
+ scope);
+ return child.parse(value);
+}
+
+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 {{
+ {"all", All::parse},
+ {"any", Any::parse},
+ {"array", ArrayAssertion::parse},
+ {"at", At::parse},
+ {"boolean", Assertion::parse},
+ {"case", Case::parse},
+ {"coalesce", Coalesce::parse},
+ {"interpolate", parseInterpolate},
+ {"let", Let::parse},
+ {"literal", Literal::parse},
+ {"match", parseMatch},
+ {"number", Assertion::parse},
+ {"object", Assertion::parse},
+ {"step", Step::parse},
+ {"string", Assertion::parse},
+ {"to-color", Coercion::parse},
+ {"to-number", Coercion::parse},
+ {"var", Var::parse}
+ }};
+ return registry;
+}
+
+ParseResult ParsingContext::parse(const Convertible& value)
+{
+ 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);
+ } else if (expected) {
+ auto wrapForType = [&](const type::Type& target, std::unique_ptr<Expression> expression) -> std::unique_ptr<Expression> {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(std::move(expression));
+ if (target == type::Color) {
+ return std::make_unique<Coercion>(target, std::move(args));
+ } else {
+ return std::make_unique<Assertion>(target, std::move(args));
+ }
+ };
+
+ const type::Type actual = (*parsed)->getType();
+ if (*expected == type::Color && (actual == type::String || actual == type::Value)) {
+ parsed = wrapForType(type::Color, std::move(*parsed));
+ } else if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean) && actual == type::Value) {
+ parsed = wrapForType(*expected, std::move(*parsed));
+ }
+
+ checkType((*parsed)->getType());
+ if (errors->size() > 0) {
+ return ParseResult();
+ }
+ }
+
+ // If an expression's arguments are all literals, we can evaluate
+ // it immediately and replace it with a literal value in the
+ // parsed result.
+ if (parsed && !dynamic_cast<Literal *>(parsed->get()) && isConstant(**parsed)) {
+ EvaluationContext params(nullptr);
+ EvaluationResult evaluated((*parsed)->evaluate(params));
+ if (!evaluated) {
+ error(evaluated.error().message);
+ return ParseResult();
+ }
+
+ const type::Type type = (*parsed)->getType();
+ if (type.is<type::Array>()) {
+ // keep the original expression's array type, even if the evaluated
+ // type is more specific.
+ return ParseResult(std::make_unique<Literal>(
+ type.get<type::Array>(),
+ evaluated->get<std::vector<Value>>())
+ );
+ } else {
+ return ParseResult(std::make_unique<Literal>(*evaluated));
+ }
+ }
+
+ // if this is the root expression, enforce constraints on the use ["zoom"].
+ if (key.size() == 0 && 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;
+}
+
+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..bfcb6fd270
--- /dev/null
+++ b/src/mbgl/style/expression/step.cpp
@@ -0,0 +1,152 @@
+#include <mbgl/style/expression/step.hpp>
+#include <mbgl/style/expression/get_covering_stops.hpp>
+#include <mbgl/util/string.hpp>
+
+
+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 (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);
+ }
+}
+
+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;
+}
+
+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)));
+}
+
+
+} // 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..f198fb3e1b
--- /dev/null
+++ b/src/mbgl/style/expression/util.cpp
@@ -0,0 +1,39 @@
+
+#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, g / 255, b / 255, 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..b75f471ce3
--- /dev/null
+++ b/src/mbgl/style/expression/value.cpp
@@ -0,0 +1,322 @@
+#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());
+}
+
+Value ValueConverter<float>::toExpressionValue(const float value) {
+ return static_cast<double>(value);
+}
+
+optional<float> ValueConverter<float>::fromExpressionValue(const Value& value) {
+ if (value.template is<double>()) {
+ double v = value.template get<double>();
+ if (v <= std::numeric_limits<float>::max()) {
+ return static_cast<float>(v);
+ }
+ }
+ return 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&);
+
+
+// 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<LightAnchorType>();
+template optional<LightAnchorType> fromExpressionValue<LightAnchorType>(const Value&);
+template Value toExpressionValue(const LightAnchorType&);
+
+template type::Type valueTypeToExpressionType<Position>();
+template optional<Position> fromExpressionValue<Position>(const Value&);
+template Value toExpressionValue(const Position&);
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/function/categorical_stops.cpp b/src/mbgl/style/function/categorical_stops.cpp
index 2984c3832f..dd179f5376 100644
--- a/src/mbgl/style/function/categorical_stops.cpp
+++ b/src/mbgl/style/function/categorical_stops.cpp
@@ -33,6 +33,9 @@ template class CategoricalStops<Color>;
template class CategoricalStops<std::array<float, 2>>;
template class CategoricalStops<std::string>;
template class CategoricalStops<TextTransformType>;
+template class CategoricalStops<TextJustifyType>;
+template class CategoricalStops<SymbolAnchorType>;
+template class CategoricalStops<LineJoinType>;
} // 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/function/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp
index 0c6891eac5..0ac6fda846 100644
--- a/src/mbgl/style/function/identity_stops.cpp
+++ b/src/mbgl/style/function/identity_stops.cpp
@@ -36,11 +36,38 @@ optional<TextTransformType> IdentityStops<TextTransformType>::evaluate(const Val
if (!value.is<std::string>()) {
return {};
}
-
+
return Enum<TextTransformType>::toEnum(value.get<std::string>());
}
template <>
+optional<TextJustifyType> IdentityStops<TextJustifyType>::evaluate(const Value& value) const {
+ if (!value.is<std::string>()) {
+ return {};
+ }
+
+ return Enum<TextJustifyType>::toEnum(value.get<std::string>());
+}
+
+template <>
+optional<SymbolAnchorType> IdentityStops<SymbolAnchorType>::evaluate(const Value& value) const {
+ if (!value.is<std::string>()) {
+ return {};
+ }
+
+ return Enum<SymbolAnchorType>::toEnum(value.get<std::string>());
+}
+
+template <>
+optional<LineJoinType> IdentityStops<LineJoinType>::evaluate(const Value& value) const {
+ if (!value.is<std::string>()) {
+ return {};
+ }
+
+ return Enum<LineJoinType>::toEnum(value.get<std::string>());
+}
+
+template <>
optional<std::array<float, 2>> IdentityStops<std::array<float, 2>>::evaluate(const Value& value) const {
if (!value.is<std::vector<Value>>()) {
return {};
diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp
index 75dc83206c..e439e42695 100644
--- a/src/mbgl/style/image_impl.hpp
+++ b/src/mbgl/style/image_impl.hpp
@@ -28,5 +28,6 @@ public:
using ImageMap = std::unordered_map<std::string, Immutable<style::Image::Impl>>;
using ImageDependencies = std::set<std::string>;
+using ImageRequestPair = std::pair<ImageDependencies, uint64_t>;
} // namespace mbgl
diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp
index 3bba135c84..9854932699 100644
--- a/src/mbgl/style/layers/circle_layer.cpp
+++ b/src/mbgl/style/layers/circle_layer.cpp
@@ -283,6 +283,33 @@ TransitionOptions CircleLayer::getCirclePitchScaleTransition() const {
return impl().paint.template get<CirclePitchScale>().options;
}
+PropertyValue<AlignmentType> CircleLayer::getDefaultCirclePitchAlignment() {
+ return { AlignmentType::Viewport };
+}
+
+PropertyValue<AlignmentType> CircleLayer::getCirclePitchAlignment() const {
+ return impl().paint.template get<CirclePitchAlignment>().value;
+}
+
+void CircleLayer::setCirclePitchAlignment(PropertyValue<AlignmentType> value) {
+ if (value == getCirclePitchAlignment())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CirclePitchAlignment>().value = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+void CircleLayer::setCirclePitchAlignmentTransition(const TransitionOptions& options) {
+ auto impl_ = mutableImpl();
+ impl_->paint.template get<CirclePitchAlignment>().options = options;
+ baseImpl = std::move(impl_);
+}
+
+TransitionOptions CircleLayer::getCirclePitchAlignmentTransition() const {
+ return impl().paint.template get<CirclePitchAlignment>().options;
+}
+
DataDrivenPropertyValue<float> CircleLayer::getDefaultCircleStrokeWidth() {
return { 0 };
}
diff --git a/src/mbgl/style/layers/circle_layer_properties.hpp b/src/mbgl/style/layers/circle_layer_properties.hpp
index 73b7028465..bc0c961e75 100644
--- a/src/mbgl/style/layers/circle_layer_properties.hpp
+++ b/src/mbgl/style/layers/circle_layer_properties.hpp
@@ -40,6 +40,10 @@ struct CirclePitchScale : PaintProperty<CirclePitchScaleType> {
static CirclePitchScaleType defaultValue() { return CirclePitchScaleType::Map; }
};
+struct CirclePitchAlignment : PaintProperty<AlignmentType> {
+ static AlignmentType defaultValue() { return AlignmentType::Viewport; }
+};
+
struct CircleStrokeWidth : DataDrivenPaintProperty<float, attributes::a_stroke_width, uniforms::u_stroke_width> {
static float defaultValue() { return 0; }
};
@@ -60,6 +64,7 @@ class CirclePaintProperties : public Properties<
CircleTranslate,
CircleTranslateAnchor,
CirclePitchScale,
+ CirclePitchAlignment,
CircleStrokeWidth,
CircleStrokeColor,
CircleStrokeOpacity
diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp
index e37382d5ef..854c771847 100644
--- a/src/mbgl/style/layers/custom_layer.cpp
+++ b/src/mbgl/style/layers/custom_layer.cpp
@@ -8,9 +8,18 @@ namespace style {
CustomLayer::CustomLayer(const std::string& layerID,
CustomLayerInitializeFunction init,
CustomLayerRenderFunction render,
+ CustomLayerContextLostFunction contextLost,
CustomLayerDeinitializeFunction deinit,
void* context)
- : Layer(makeMutable<Impl>(layerID, init, render, deinit, 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)) {
}
CustomLayer::~CustomLayer() = default;
diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp
index 42e60c582c..1de268d2e2 100644
--- a/src/mbgl/style/layers/custom_layer_impl.cpp
+++ b/src/mbgl/style/layers/custom_layer_impl.cpp
@@ -6,12 +6,14 @@ namespace style {
CustomLayer::Impl::Impl(const std::string& id_,
CustomLayerInitializeFunction initializeFn_,
CustomLayerRenderFunction renderFn_,
+ CustomLayerContextLostFunction contextLostFn_,
CustomLayerDeinitializeFunction deinitializeFn_,
void* context_)
: Layer::Impl(LayerType::Custom, id_, std::string()) {
initializeFn = initializeFn_;
renderFn = renderFn_;
deinitializeFn = deinitializeFn_;
+ contextLostFn = contextLostFn_;
context = context_;
}
diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp
index defbbe6894..62efbbe15b 100644
--- a/src/mbgl/style/layers/custom_layer_impl.hpp
+++ b/src/mbgl/style/layers/custom_layer_impl.hpp
@@ -14,6 +14,7 @@ public:
Impl(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
+ CustomLayerContextLostFunction,
CustomLayerDeinitializeFunction,
void* context);
@@ -22,6 +23,7 @@ public:
CustomLayerInitializeFunction initializeFn = nullptr;
CustomLayerRenderFunction renderFn = nullptr;
+ CustomLayerContextLostFunction contextLostFn = nullptr;
CustomLayerDeinitializeFunction deinitializeFn = nullptr;
void* context = nullptr;
};
diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp
index 6fbdf19568..1c7f0d28ee 100644
--- a/src/mbgl/style/layers/line_layer.cpp
+++ b/src/mbgl/style/layers/line_layer.cpp
@@ -108,15 +108,15 @@ void LineLayer::setLineCap(PropertyValue<LineCapType> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<LineJoinType> LineLayer::getDefaultLineJoin() {
+DataDrivenPropertyValue<LineJoinType> LineLayer::getDefaultLineJoin() {
return LineJoin::defaultValue();
}
-PropertyValue<LineJoinType> LineLayer::getLineJoin() const {
+DataDrivenPropertyValue<LineJoinType> LineLayer::getLineJoin() const {
return impl().layout.get<LineJoin>();
}
-void LineLayer::setLineJoin(PropertyValue<LineJoinType> value) {
+void LineLayer::setLineJoin(DataDrivenPropertyValue<LineJoinType> value) {
if (value == getLineJoin())
return;
auto impl_ = mutableImpl();
diff --git a/src/mbgl/style/layers/line_layer_properties.hpp b/src/mbgl/style/layers/line_layer_properties.hpp
index b2c7f3199c..aeaf51698a 100644
--- a/src/mbgl/style/layers/line_layer_properties.hpp
+++ b/src/mbgl/style/layers/line_layer_properties.hpp
@@ -17,7 +17,7 @@ struct LineCap : LayoutProperty<LineCapType> {
static LineCapType defaultValue() { return LineCapType::Butt; }
};
-struct LineJoin : LayoutProperty<LineJoinType> {
+struct LineJoin : DataDrivenLayoutProperty<LineJoinType> {
static constexpr const char * key = "line-join";
static LineJoinType defaultValue() { return LineJoinType::Miter; }
};
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index 182b4b0a48..9a944657ca 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -332,6 +332,38 @@ void SymbolLayer::setIconOffset(DataDrivenPropertyValue<std::array<float, 2>> va
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultIconAnchor() {
+ return IconAnchor::defaultValue();
+}
+
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getIconAnchor() const {
+ return impl().layout.get<IconAnchor>();
+}
+
+void SymbolLayer::setIconAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) {
+ if (value == getIconAnchor())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconAnchor>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+PropertyValue<AlignmentType> SymbolLayer::getDefaultIconPitchAlignment() {
+ return IconPitchAlignment::defaultValue();
+}
+
+PropertyValue<AlignmentType> SymbolLayer::getIconPitchAlignment() const {
+ return impl().layout.get<IconPitchAlignment>();
+}
+
+void SymbolLayer::setIconPitchAlignment(PropertyValue<AlignmentType> value) {
+ if (value == getIconPitchAlignment())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconPitchAlignment>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
PropertyValue<AlignmentType> SymbolLayer::getDefaultTextPitchAlignment() {
return TextPitchAlignment::defaultValue();
}
@@ -412,15 +444,15 @@ void SymbolLayer::setTextSize(DataDrivenPropertyValue<float> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() {
+DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextMaxWidth() {
return TextMaxWidth::defaultValue();
}
-PropertyValue<float> SymbolLayer::getTextMaxWidth() const {
+DataDrivenPropertyValue<float> SymbolLayer::getTextMaxWidth() const {
return impl().layout.get<TextMaxWidth>();
}
-void SymbolLayer::setTextMaxWidth(PropertyValue<float> value) {
+void SymbolLayer::setTextMaxWidth(DataDrivenPropertyValue<float> value) {
if (value == getTextMaxWidth())
return;
auto impl_ = mutableImpl();
@@ -444,15 +476,15 @@ void SymbolLayer::setTextLineHeight(PropertyValue<float> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() {
+DataDrivenPropertyValue<float> SymbolLayer::getDefaultTextLetterSpacing() {
return TextLetterSpacing::defaultValue();
}
-PropertyValue<float> SymbolLayer::getTextLetterSpacing() const {
+DataDrivenPropertyValue<float> SymbolLayer::getTextLetterSpacing() const {
return impl().layout.get<TextLetterSpacing>();
}
-void SymbolLayer::setTextLetterSpacing(PropertyValue<float> value) {
+void SymbolLayer::setTextLetterSpacing(DataDrivenPropertyValue<float> value) {
if (value == getTextLetterSpacing())
return;
auto impl_ = mutableImpl();
@@ -460,15 +492,15 @@ void SymbolLayer::setTextLetterSpacing(PropertyValue<float> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<TextJustifyType> SymbolLayer::getDefaultTextJustify() {
+DataDrivenPropertyValue<TextJustifyType> SymbolLayer::getDefaultTextJustify() {
return TextJustify::defaultValue();
}
-PropertyValue<TextJustifyType> SymbolLayer::getTextJustify() const {
+DataDrivenPropertyValue<TextJustifyType> SymbolLayer::getTextJustify() const {
return impl().layout.get<TextJustify>();
}
-void SymbolLayer::setTextJustify(PropertyValue<TextJustifyType> value) {
+void SymbolLayer::setTextJustify(DataDrivenPropertyValue<TextJustifyType> value) {
if (value == getTextJustify())
return;
auto impl_ = mutableImpl();
@@ -476,15 +508,15 @@ void SymbolLayer::setTextJustify(PropertyValue<TextJustifyType> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<TextAnchorType> SymbolLayer::getDefaultTextAnchor() {
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getDefaultTextAnchor() {
return TextAnchor::defaultValue();
}
-PropertyValue<TextAnchorType> SymbolLayer::getTextAnchor() const {
+DataDrivenPropertyValue<SymbolAnchorType> SymbolLayer::getTextAnchor() const {
return impl().layout.get<TextAnchor>();
}
-void SymbolLayer::setTextAnchor(PropertyValue<TextAnchorType> value) {
+void SymbolLayer::setTextAnchor(DataDrivenPropertyValue<SymbolAnchorType> value) {
if (value == getTextAnchor())
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 f7ceaecdaa..436b5cbd4f 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.hpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.hpp
@@ -87,6 +87,16 @@ struct IconOffset : DataDrivenLayoutProperty<std::array<float, 2>> {
static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; }
};
+struct IconAnchor : DataDrivenLayoutProperty<SymbolAnchorType> {
+ static constexpr const char * key = "icon-anchor";
+ static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; }
+};
+
+struct IconPitchAlignment : LayoutProperty<AlignmentType> {
+ static constexpr const char * key = "icon-pitch-alignment";
+ static AlignmentType defaultValue() { return AlignmentType::Auto; }
+};
+
struct TextPitchAlignment : LayoutProperty<AlignmentType> {
static constexpr const char * key = "text-pitch-alignment";
static AlignmentType defaultValue() { return AlignmentType::Auto; }
@@ -112,7 +122,7 @@ struct TextSize : DataDrivenLayoutProperty<float> {
static float defaultValue() { return 16; }
};
-struct TextMaxWidth : LayoutProperty<float> {
+struct TextMaxWidth : DataDrivenLayoutProperty<float> {
static constexpr const char * key = "text-max-width";
static float defaultValue() { return 10; }
};
@@ -122,19 +132,19 @@ struct TextLineHeight : LayoutProperty<float> {
static float defaultValue() { return 1.2; }
};
-struct TextLetterSpacing : LayoutProperty<float> {
+struct TextLetterSpacing : DataDrivenLayoutProperty<float> {
static constexpr const char * key = "text-letter-spacing";
static float defaultValue() { return 0; }
};
-struct TextJustify : LayoutProperty<TextJustifyType> {
+struct TextJustify : DataDrivenLayoutProperty<TextJustifyType> {
static constexpr const char * key = "text-justify";
static TextJustifyType defaultValue() { return TextJustifyType::Center; }
};
-struct TextAnchor : LayoutProperty<TextAnchorType> {
+struct TextAnchor : DataDrivenLayoutProperty<SymbolAnchorType> {
static constexpr const char * key = "text-anchor";
- static TextAnchorType defaultValue() { return TextAnchorType::Center; }
+ static SymbolAnchorType defaultValue() { return SymbolAnchorType::Center; }
};
struct TextMaxAngle : LayoutProperty<float> {
@@ -254,6 +264,8 @@ class SymbolLayoutProperties : public Properties<
IconPadding,
IconKeepUpright,
IconOffset,
+ IconAnchor,
+ IconPitchAlignment,
TextPitchAlignment,
TextRotationAlignment,
TextField,
diff --git a/src/mbgl/style/observer.hpp b/src/mbgl/style/observer.hpp
index ea19c599e9..cc6378b366 100644
--- a/src/mbgl/style/observer.hpp
+++ b/src/mbgl/style/observer.hpp
@@ -1,7 +1,6 @@
#pragma once
#include <mbgl/style/source_observer.hpp>
-#include <mbgl/map/update.hpp>
#include <exception>
@@ -12,7 +11,7 @@ class Observer : public SourceObserver {
public:
virtual void onStyleLoading() {}
virtual void onStyleLoaded() {}
- virtual void onUpdate(Update) {}
+ virtual void onUpdate() {}
virtual void onStyleError(std::exception_ptr) {}
virtual void onResourceError(std::exception_ptr) {}
};
diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp
index 467b44632c..10fce33986 100644
--- a/src/mbgl/style/parser.cpp
+++ b/src/mbgl/style/parser.cpp
@@ -1,13 +1,16 @@
#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>
#include <mapbox/geojsonvt.hpp>
@@ -148,7 +151,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 =
@@ -255,7 +258,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);
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/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp
index be347af2ab..fd6d7d3013 100644
--- a/src/mbgl/style/sources/geojson_source_impl.cpp
+++ b/src/mbgl/style/sources/geojson_source_impl.cpp
@@ -1,10 +1,13 @@
#include <mbgl/style/sources/geojson_source_impl.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/util/string.hpp>
#include <mapbox/geojsonvt.hpp>
#include <supercluster.hpp>
+#include <cmath>
+
namespace mbgl {
namespace style {
@@ -52,14 +55,14 @@ GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON)
mapbox::supercluster::Options clusterOptions;
clusterOptions.maxZoom = options.clusterMaxZoom;
clusterOptions.extent = util::EXTENT;
- clusterOptions.radius = std::round(scale * options.clusterRadius);
+ clusterOptions.radius = ::round(scale * options.clusterRadius);
data = std::make_unique<SuperclusterData>(
geoJSON.get<mapbox::geometry::feature_collection<double>>(), clusterOptions);
} else {
mapbox::geojsonvt::Options vtOptions;
vtOptions.maxZoom = options.maxzoom;
vtOptions.extent = util::EXTENT;
- vtOptions.buffer = std::round(scale * options.buffer);
+ vtOptions.buffer = ::round(scale * options.buffer);
vtOptions.tolerance = scale * options.tolerance;
data = std::make_unique<GeoJSONVTData>(geoJSON, vtOptions);
}
@@ -68,7 +71,7 @@ GeoJSONSource::Impl::Impl(const Impl& other, const GeoJSON& geoJSON)
GeoJSONSource::Impl::~Impl() = default;
Range<uint8_t> GeoJSONSource::Impl::getZoomRange() const {
- return { 0, options.maxzoom };
+ return { options.minzoom, options.maxzoom };
}
GeoJSONData* GeoJSONSource::Impl::getData() const {
diff --git a/src/mbgl/style/sources/image_source.cpp b/src/mbgl/style/sources/image_source.cpp
index 9313d8da4a..fa268da0ef 100644
--- a/src/mbgl/style/sources/image_source.cpp
+++ b/src/mbgl/style/sources/image_source.cpp
@@ -37,7 +37,7 @@ void ImageSource::setURL(const std::string& url_) {
}
}
-void ImageSource::setImage(UnassociatedImage&& image_) {
+void ImageSource::setImage(PremultipliedImage&& image_) {
url = {};
if (req) {
req.reset();
@@ -59,7 +59,7 @@ void ImageSource::loadDescription(FileSource& fileSource) {
if (req || loaded) {
return;
}
- const Resource imageResource { Resource::Image, *url, {}, Resource::Necessity::Required };
+ const Resource imageResource { Resource::Image, *url, {} };
req = fileSource.request(imageResource, [this](Response res) {
if (res.error) {
@@ -70,8 +70,7 @@ void ImageSource::loadDescription(FileSource& fileSource) {
observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty image url")));
} else {
try {
- UnassociatedImage image = util::unpremultiply(decodeImage(*res.data));
- baseImpl = makeMutable<Impl>(impl(), std::move(image));
+ baseImpl = makeMutable<Impl>(impl(), decodeImage(*res.data));
} catch (...) {
observer->onSourceError(*this, std::current_exception());
}
diff --git a/src/mbgl/style/sources/image_source_impl.cpp b/src/mbgl/style/sources/image_source_impl.cpp
index 98f3cc9db9..c1f31dbdc6 100644
--- a/src/mbgl/style/sources/image_source_impl.cpp
+++ b/src/mbgl/style/sources/image_source_impl.cpp
@@ -12,17 +12,17 @@ ImageSource::Impl::Impl(std::string id_, std::array<LatLng, 4> coords_)
ImageSource::Impl::Impl(const Impl& other, std::array<LatLng, 4> coords_)
: Source::Impl(other),
coords(std::move(coords_)),
- image(other.image.clone()) {
+ image(other.image) {
}
-ImageSource::Impl::Impl(const Impl& rhs, UnassociatedImage image_)
+ImageSource::Impl::Impl(const Impl& rhs, PremultipliedImage&& image_)
: Source::Impl(rhs),
coords(rhs.coords),
- image(std::move(image_)) {
+ image(std::make_shared<PremultipliedImage>(std::move(image_))) {
}
ImageSource::Impl::~Impl() = default;
-const UnassociatedImage& ImageSource::Impl::getImage() const {
+std::shared_ptr<PremultipliedImage> ImageSource::Impl::getImage() const {
return image;
}
diff --git a/src/mbgl/style/sources/image_source_impl.hpp b/src/mbgl/style/sources/image_source_impl.hpp
index 5fd41ac6e6..1e1b005a32 100644
--- a/src/mbgl/style/sources/image_source_impl.hpp
+++ b/src/mbgl/style/sources/image_source_impl.hpp
@@ -13,17 +13,17 @@ class ImageSource::Impl : public Source::Impl {
public:
Impl(std::string id, std::array<LatLng, 4> coords);
Impl(const Impl& rhs, std::array<LatLng, 4> coords);
- Impl(const Impl& rhs, UnassociatedImage image);
+ Impl(const Impl& rhs, PremultipliedImage&& image);
~Impl() final;
- const UnassociatedImage& getImage() const;
+ std::shared_ptr<PremultipliedImage> getImage() const;
std::array<LatLng, 4> getCoordinates() const;
optional<std::string> getAttribution() const final;
private:
std::array<LatLng, 4> coords;
- UnassociatedImage image;
+ std::shared_ptr<PremultipliedImage> image;
};
} // namespace style
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 5fe1ab4a06..bd8631fc52 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -34,20 +34,8 @@ std::string Style::getName() const {
return impl->getName();
}
-LatLng Style::getDefaultLatLng() const {
- return impl->getDefaultLatLng();
-}
-
-double Style::getDefaultZoom() const {
- return impl->getDefaultZoom();
-}
-
-double Style::getDefaultBearing() const {
- return impl->getDefaultBearing();
-}
-
-double Style::getDefaultPitch() const {
- return impl->getDefaultPitch();
+CameraOptions Style::getDefaultCamera() const {
+ return impl->getDefaultCamera();
}
TransitionOptions Style::getTransitionOptions() const {
diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp
index 2d42afd086..3214c6316e 100644
--- a/src/mbgl/style/style_impl.cpp
+++ b/src/mbgl/style/style_impl.cpp
@@ -38,6 +38,7 @@ Style::Impl::Impl(Scheduler& scheduler_, FileSource& fileSource_, float pixelRat
Style::Impl::~Impl() = default;
void Style::Impl::loadJSON(const std::string& json_) {
+ lastError = nullptr;
observer->onStyleLoading();
url.clear();
@@ -45,6 +46,7 @@ void Style::Impl::loadJSON(const std::string& json_) {
}
void Style::Impl::loadURL(const std::string& url_) {
+ lastError = nullptr;
observer->onStyleLoading();
loaded = false;
@@ -86,7 +88,7 @@ void Style::Impl::parse(const std::string& json_) {
}
mutated = false;
- loaded = true;
+ loaded = false;
json = json_;
sources.clear();
@@ -105,15 +107,18 @@ void Style::Impl::parse(const std::string& json_) {
}
name = parser.name;
- defaultLatLng = parser.latLng;
- defaultZoom = parser.zoom;
- defaultBearing = parser.bearing;
- defaultPitch = parser.pitch;
+ defaultCamera.center = parser.latLng;
+ defaultCamera.zoom = parser.zoom;
+ defaultCamera.angle = parser.bearing;
+ defaultCamera.pitch = parser.pitch;
+
setLight(std::make_unique<Light>(parser.light));
+ spriteLoaded = false;
spriteLoader->load(parser.spriteURL, scheduler, fileSource);
glyphURL = parser.glyphURL;
+ loaded = true;
observer->onStyleLoaded();
}
@@ -199,9 +204,10 @@ Layer* Style::Impl::addLayer(std::unique_ptr<Layer> layer, optional<std::string>
}
layer->setObserver(this);
- observer->onUpdate(Update::Repaint);
+ Layer* result = layers.add(std::move(layer), before);
+ observer->onUpdate();
- return layers.add(std::move(layer), before);
+ return result;
}
std::unique_ptr<Layer> Style::Impl::removeLayer(const std::string& id) {
@@ -209,7 +215,7 @@ std::unique_ptr<Layer> Style::Impl::removeLayer(const std::string& id) {
if (layer) {
layer->setObserver(nullptr);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
return layer;
@@ -229,20 +235,8 @@ std::string Style::Impl::getName() const {
return name;
}
-LatLng Style::Impl::getDefaultLatLng() const {
- return defaultLatLng;
-}
-
-double Style::Impl::getDefaultZoom() const {
- return defaultZoom;
-}
-
-double Style::Impl::getDefaultBearing() const {
- return defaultBearing;
-}
-
-double Style::Impl::getDefaultPitch() const {
- return defaultPitch;
+CameraOptions Style::Impl::getDefaultCamera() const {
+ return defaultCamera;
}
std::vector<Source*> Style::Impl::getSources() {
@@ -296,13 +290,13 @@ void Style::Impl::setObserver(style::Observer* observer_) {
void Style::Impl::onSourceLoaded(Source& source) {
sources.update(source);
observer->onSourceLoaded(source);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onSourceChanged(Source& source) {
sources.update(source);
observer->onSourceChanged(source);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onSourceError(Source& source, std::exception_ptr error) {
@@ -326,7 +320,7 @@ void Style::Impl::onSpriteLoaded(std::vector<std::unique_ptr<Image>>&& images_)
addImage(std::move(image));
}
spriteLoaded = true;
- observer->onUpdate(Update::Repaint); // For *-pattern properties.
+ observer->onUpdate(); // For *-pattern properties.
}
void Style::Impl::onSpriteError(std::exception_ptr error) {
@@ -337,11 +331,11 @@ void Style::Impl::onSpriteError(std::exception_ptr error) {
void Style::Impl::onLayerChanged(Layer& layer) {
layers.update(layer);
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::onLightChanged(const Light&) {
- observer->onUpdate(Update::Repaint);
+ observer->onUpdate();
}
void Style::Impl::dumpDebugLogs() const {
diff --git a/src/mbgl/style/style_impl.hpp b/src/mbgl/style/style_impl.hpp
index 76f244d5a4..3dc222bfad 100644
--- a/src/mbgl/style/style_impl.hpp
+++ b/src/mbgl/style/style_impl.hpp
@@ -12,6 +12,8 @@
#include <mbgl/style/layer.hpp>
#include <mbgl/style/collection.hpp>
+#include <mbgl/map/camera.hpp>
+
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/geo.hpp>
@@ -69,10 +71,7 @@ public:
std::unique_ptr<Layer> removeLayer(const std::string& layerID);
std::string getName() const;
- LatLng getDefaultLatLng() const;
- double getDefaultZoom() const;
- double getDefaultBearing() const;
- double getDefaultPitch() const;
+ CameraOptions getDefaultCamera() const;
TransitionOptions getTransitionOptions() const;
void setTransitionOptions(const TransitionOptions&);
@@ -117,10 +116,7 @@ private:
// Defaults
std::string name;
- LatLng defaultLatLng;
- double defaultZoom = 0;
- double defaultBearing = 0;
- double defaultPitch = 0;
+ CameraOptions defaultCamera;
// SpriteLoaderObserver implementation.
void onSpriteLoaded(std::vector<std::unique_ptr<Image>>&&) override;
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
index 4fbf767e11..cd5e597fc0 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, {
@@ -53,16 +54,16 @@ MBGL_DEFINE_ENUM(SymbolPlacementType, {
{ SymbolPlacementType::Line, "line" },
});
-MBGL_DEFINE_ENUM(TextAnchorType, {
- { TextAnchorType::Center, "center" },
- { TextAnchorType::Left, "left" },
- { TextAnchorType::Right, "right" },
- { TextAnchorType::Top, "top" },
- { TextAnchorType::Bottom, "bottom" },
- { TextAnchorType::TopLeft, "top-left" },
- { TextAnchorType::TopRight, "top-right" },
- { TextAnchorType::BottomLeft, "bottom-left" },
- { TextAnchorType::BottomRight, "bottom-right" }
+MBGL_DEFINE_ENUM(SymbolAnchorType, {
+ { SymbolAnchorType::Center, "center" },
+ { SymbolAnchorType::Left, "left" },
+ { SymbolAnchorType::Right, "right" },
+ { SymbolAnchorType::Top, "top" },
+ { SymbolAnchorType::Bottom, "bottom" },
+ { SymbolAnchorType::TopLeft, "top-left" },
+ { SymbolAnchorType::TopRight, "top-right" },
+ { SymbolAnchorType::BottomLeft, "bottom-left" },
+ { SymbolAnchorType::BottomRight, "bottom-right" }
});
MBGL_DEFINE_ENUM(TextJustifyType, {
diff --git a/src/mbgl/text/collision_feature.cpp b/src/mbgl/text/collision_feature.cpp
index 71d7cc74e0..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,25 +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, 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 unsigned int nBoxes = std::floor(labelLength / step);
+ const int nBoxes = std::floor(labelLength / step);
+ // 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
+ // 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.
@@ -58,24 +60,46 @@ void CollisionFeature::bboxifyLabel(const GeometryCoordinates& line, GeometryCoo
GeometryCoordinate &p = anchorPoint;
int index = segment + 1;
float anchorDistance = firstBoxOffset;
+ const float labelStartDistance = -labelLength / 2;
+ const float paddingStartDistance = labelStartDistance - labelLength / 8;
// move backwards along the line to the first segment the label appears on
do {
index--;
- // there isn't enough room for the label after the beginning of the line
- // checkMaxAngle should have already caught this
- if (index < 0) return;
+ if (index < 0) {
+ if (anchorDistance > labelStartDistance) {
+ // there isn't enough room for the label after the beginning of the line
+ // checkMaxAngle should have already caught this
+ return;
+ } else {
+ // The line doesn't extend far enough back for all of our padding,
+ // but we got far enough to show the label under most conditions.
+ index = 0;
+ break;
+ }
+ }
anchorDistance -= util::dist<float>(line[index], p);
p = line[index];
- } while (anchorDistance > -labelLength / 2);
+ } while (anchorDistance > paddingStartDistance);
auto segmentLength = util::dist<float>(line[index], line[index + 1]);
- for (unsigned int i = 0; i < nBoxes; i++) {
+ for (int i = -nPitchPaddingBoxes; i < nBoxes + nPitchPaddingBoxes; i++) {
// the distance the box will be from the anchor
- const float boxDistanceToAnchor = -labelLength / 2 + i * step;
+ const float boxOffset = i * step;
+ float boxDistanceToAnchor = labelStartDistance + boxOffset;
+
+ // make the distance between pitch padding boxes bigger
+ if (boxOffset < 0) boxDistanceToAnchor += boxOffset;
+ if (boxOffset > labelLength) boxDistanceToAnchor += boxOffset - labelLength;
+
+ if (boxDistanceToAnchor < anchorDistance) {
+ // The line doesn't extend far enough back for this box, skip it
+ // (This could allow for line collisions on distant tiles)
+ continue;
+ }
// the box is not on the current segment. Move to the next segment.
while (anchorDistance + segmentLength < boxDistanceToAnchor) {
@@ -98,12 +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)
};
-
- const float distanceToInnerEdge = std::max(std::fabs(boxDistanceToAnchor - firstBoxOffset) - step / 2, 0.0f);
- const float maxScale = labelLength / 2 / distanceToInnerEdge;
-
- boxes.emplace_back(boxAnchor, -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);
}
}
+
} // namespace mbgl
diff --git a/src/mbgl/text/collision_feature.hpp b/src/mbgl/text/collision_feature.hpp
index c94ec23513..df1b12819c 100644
--- a/src/mbgl/text/collision_feature.hpp
+++ b/src/mbgl/text/collision_feature.hpp
@@ -11,11 +11,14 @@ namespace mbgl {
class CollisionBox {
public:
- CollisionBox(Point<float> _anchor, float _x1, float _y1, float _x2, float _y2, float _maxScale) :
- anchor(std::move(_anchor)), x1(_x1), y1(_y1), x2(_x2), y2(_y2), maxScale(_maxScale) {}
+ 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;
+
+ // the offset of the box from the label's anchor point
+ Point<float> offset;
// distances to the edges from the anchor
float x1;
@@ -23,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,
@@ -45,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&,
@@ -73,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..8014efe6c7
--- /dev/null
+++ b/src/mbgl/text/collision_index.cpp
@@ -0,0 +1,359 @@
+#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;
+ // equivalent to pixel_to_tile_units
+ const auto pixelsToTileUnits = tileToViewport / scale;
+
+ float firstTileDistance = 0, lastTileDistance = 0;
+ if (firstAndLastGlyph) {
+ firstTileDistance = approximateTileDistance(*(firstAndLastGlyph->first.tileDistance), firstAndLastGlyph->first.angle, pixelsToTileUnits, projectedAnchor.second, pitchWithMap);
+ lastTileDistance = approximateTileDistance(*(firstAndLastGlyph->second.tileDistance), firstAndLastGlyph->second.angle, pixelsToTileUnits, projectedAnchor.second, pitchWithMap);
+ }
+
+ bool atLeastOneCirclePlaced = false;
+ for (size_t i = 0; i < feature.boxes.size(); i++) {
+ CollisionBox& circle = feature.boxes[i];
+ const float boxSignedDistanceFromAnchor = circle.signedDistanceFromAnchor;
+ if (!firstAndLastGlyph ||
+ (boxSignedDistanceFromAnchor < -firstTileDistance) ||
+ (boxSignedDistanceFromAnchor > lastTileDistance)) {
+ // The label either doesn't fit on its line or we
+ // don't need to use this circle because the label
+ // doesn't extend this far. Either way, mark the circle unused.
+ circle.used = false;
+ continue;
+ }
+
+ const auto projectedPoint = projectPoint(posMatrix, circle.anchor);
+ const float tileUnitRadius = (circle.x2 - circle.x1) / 2;
+ const float radius = tileUnitRadius / tileToViewport;
+
+ if (atLeastOneCirclePlaced) {
+ const CollisionBox& previousCircle = feature.boxes[i - 1];
+ const float dx = projectedPoint.x - previousCircle.px;
+ const float dy = projectedPoint.y - previousCircle.py;
+ // The circle edges touch when the distance between their centers is 2x the radius
+ // When the distance is 1x the radius, they're doubled up, and we could remove
+ // every other circle while keeping them all in touch.
+ // We actually start removing circles when the distance is √2x the radius:
+ // thinning the number of circles as much as possible is a major performance win,
+ // and the small gaps introduced don't make a very noticeable difference.
+ const bool placedTooDensely = radius * radius * 2 > dx * dx + dy * dy;
+ if (placedTooDensely) {
+ const bool atLeastOneMoreCircle = (i + 1) < feature.boxes.size();
+ if (atLeastOneMoreCircle) {
+ const CollisionBox& nextCircle = feature.boxes[i + 1];
+ const float nextBoxDistanceFromAnchor = nextCircle.signedDistanceFromAnchor;
+ if ((nextBoxDistanceFromAnchor > -firstTileDistance) &&
+ (nextBoxDistanceFromAnchor < lastTileDistance)) {
+ // Hide significantly overlapping circles, unless this is the last one we can
+ // use, in which case we want to keep it in place even if it's tightly packed
+ // with the one before it.
+ circle.used = false;
+ continue;
+ }
+ }
+ }
+ }
+
+ atLeastOneCirclePlaced = true;
+ circle.px1 = projectedPoint.x - radius;
+ circle.px2 = projectedPoint.x + radius;
+ circle.py1 = projectedPoint.y - radius;
+ circle.py2 = projectedPoint.y + radius;
+
+ circle.used = true;
+
+ circle.px = projectedPoint.x;
+ circle.py = projectedPoint.y;
+ circle.radius = radius;
+
+ entirelyOffscreen &= isOffscreen(circle);
+ inGrid |= isInsideGrid(circle);
+
+ if (!allowOverlap) {
+ if (collisionGrid.hitTest({{circle.px, circle.py}, circle.radius})) {
+ if (!collisionDebug) {
+ return {false, false};
+ } else {
+ // Don't early exit if we're showing the debug circles because we still want to calculate
+ // which circles are in use
+ collisionDetected = true;
+ }
+ }
+ }
+ }
+
+ return {!collisionDetected && firstAndLastGlyph && inGrid, entirelyOffscreen};
+}
+
+
+void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement) {
+ if (feature.alongLine) {
+ for (auto& circle : feature.boxes) {
+ if (!circle.used) {
+ continue;
+ }
+
+ if (ignorePlacement) {
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius});
+ } else {
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius});
+ }
+ }
+ } else {
+ assert(feature.boxes.size() == 1);
+ auto& box = feature.boxes[0];
+ if (ignorePlacement) {
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
+ } else {
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
+ }
+ }
+}
+
+bool polygonIntersectsBox(const LineString<float>& polygon, const GridIndex<IndexedSubfeature>::BBox& bbox) {
+ // This is just a wrapper that allows us to use the integer-based util::polygonIntersectsPolygon
+ // Conversion limits our query accuracy to single-pixel resolution
+ GeometryCoordinates integerPolygon;
+ for (const auto& point : polygon) {
+ integerPolygon.push_back(convertPoint<int16_t>(point));
+ }
+ int16_t minX1 = bbox.min.x;
+ int16_t maxY1 = bbox.max.y;
+ int16_t minY1 = bbox.min.y;
+ int16_t maxX1 = bbox.max.x;
+
+ auto bboxPoints = GeometryCoordinates {
+ { minX1, minY1 }, { maxX1, minY1 }, { maxX1, maxY1 }, { minX1, maxY1 }
+ };
+
+ return util::polygonIntersectsPolygon(integerPolygon, bboxPoints);
+}
+
+std::vector<IndexedSubfeature> CollisionIndex::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const UnwrappedTileID& tileID, const std::string& sourceID) const {
+ std::vector<IndexedSubfeature> result;
+ if (queryGeometry.empty() || (collisionGrid.empty() && ignoredGrid.empty())) {
+ return result;
+ }
+
+ mat4 posMatrix;
+ mat4 projMatrix;
+ transformState.getProjMatrix(projMatrix);
+ transformState.matrixFor(posMatrix, tileID);
+ matrix::multiply(posMatrix, projMatrix, posMatrix);
+
+ // queryGeometry is specified in integer tile units, but in projecting we switch to float pixels
+ LineString<float> projectedQuery;
+ for (const auto& point : queryGeometry) {
+ auto projected = projectPoint(posMatrix, convertPoint<float>(point));
+ projectedQuery.push_back(projected);
+ }
+
+ auto envelope = mapbox::geometry::envelope(projectedQuery);
+
+ using QueryResult = std::pair<IndexedSubfeature, GridIndex<IndexedSubfeature>::BBox>;
+
+ std::vector<QueryResult> thisTileFeatures;
+ std::vector<QueryResult> features = collisionGrid.queryWithBoxes(envelope);
+
+ for (auto& queryResult : features) {
+ auto& feature = queryResult.first;
+ if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) {
+ // We only have to filter on the canonical ID because even if the feature is showing multiple times
+ // we treat it as one feature.
+ thisTileFeatures.push_back(queryResult);
+ }
+ }
+
+ std::vector<QueryResult> ignoredFeatures = ignoredGrid.queryWithBoxes(envelope);
+ for (auto& queryResult : ignoredFeatures) {
+ auto& feature = queryResult.first;
+ if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) {
+ thisTileFeatures.push_back(queryResult);
+ }
+ }
+
+ std::unordered_map<std::string, std::unordered_map<std::string, std::unordered_set<std::size_t>>> sourceLayerFeatures;
+ for (auto& queryResult : thisTileFeatures) {
+ auto& feature = queryResult.first;
+ auto& bbox = queryResult.second;
+
+ // Skip already seen features.
+ auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName][feature.bucketName];
+ if (seenFeatures.find(feature.index) != seenFeatures.end())
+ continue;
+
+ seenFeatures.insert(feature.index);
+
+ if (!polygonIntersectsBox(projectedQuery, bbox)) {
+ continue;
+ }
+
+ result.push_back(feature);
+ }
+
+ return result;
+
+}
+
+std::pair<float,float> CollisionIndex::projectAnchor(const mat4& posMatrix, const Point<float>& point) const {
+ vec4 p = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(p, p, posMatrix);
+ return std::make_pair(
+ 0.5 + 0.5 * (p[3] / transformState.getCameraToCenterDistance()),
+ 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
+ ),
+ 0.5 + 0.5 * (p[3] / transformState.getCameraToCenterDistance())
+ );
+}
+
+Point<float> CollisionIndex::projectPoint(const mat4& posMatrix, const Point<float>& point) const {
+ vec4 p = {{ point.x, point.y, 0, 1 }};
+ matrix::transformMat4(p, p, posMatrix);
+ return Point<float>(
+ (((p[0] / p[3] + 1) / 2) * transformState.getSize().width) + viewportPadding,
+ (((-p[1] / p[3] + 1) / 2) * transformState.getSize().height) + viewportPadding
+ );
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp
new file mode 100644
index 0000000000..8653c1d76c
--- /dev/null
+++ b/src/mbgl/text/collision_index.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/text/collision_feature.hpp>
+#include <mbgl/util/grid_index.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+namespace mbgl {
+
+class PlacedSymbol;
+
+struct TileDistance;
+
+class CollisionIndex {
+public:
+ using CollisionGrid = GridIndex<IndexedSubfeature>;
+
+ explicit CollisionIndex(const TransformState&);
+
+ std::pair<bool,bool> placeFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug);
+
+ void insertFeature(CollisionFeature& feature, bool ignorePlacement);
+
+ std::vector<IndexedSubfeature> queryRenderedSymbols(const GeometryCoordinates&, const UnwrappedTileID& tileID, const std::string& sourceID) const;
+
+
+private:
+ bool isOffscreen(const CollisionBox&) const;
+ bool isInsideGrid(const CollisionBox&) const;
+
+ std::pair<bool,bool> placeLineFeature(CollisionFeature& feature,
+ const mat4& posMatrix,
+ const mat4& labelPlaneMatrix,
+ const float textPixelRatio,
+ PlacedSymbol& symbol,
+ const float scale,
+ const float fontSize,
+ const bool allowOverlap,
+ const bool pitchWithMap,
+ const bool collisionDebug);
+
+ float approximateTileDistance(const TileDistance& tileDistance, const float lastSegmentAngle, const float pixelsToTileUnits, const float cameraToAnchorDistance, const bool pitchWithMap);
+
+ std::pair<float,float> projectAnchor(const mat4& posMatrix, const Point<float>& point) const;
+ std::pair<Point<float>,float> projectAndGetPerspectiveRatio(const mat4& posMatrix, const Point<float>& point) const;
+ Point<float> projectPoint(const mat4& posMatrix, const Point<float>& point) const;
+
+ const TransformState transformState;
+
+ CollisionGrid collisionGrid;
+ CollisionGrid ignoredGrid;
+
+ const float screenRightBoundary;
+ const float screenBottomBoundary;
+ const float gridRightBoundary;
+ const float gridBottomBoundary;
+
+ const float pitchFactor;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp
deleted file mode 100644
index 368750c89f..0000000000
--- a/src/mbgl/text/collision_tile.cpp
+++ /dev/null
@@ -1,235 +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 } };
-
- // Stretch boxes in y direction to account for the map tilt.
- const float _yStretch = 1.0f / std::cos(config.pitch);
-
- // The amount the map is squished depends on the y position.
- // Sort of account for this by making all boxes a bit bigger.
- yStretch = std::pow(_yStretch, 1.3f);
-}
-
-
-float CollisionTile::findPlacementScale(const Point<float>& anchor, const CollisionBox& box, 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:
- float s1 = (blocking.x1 - box.x2) / (anchor.x - blockingAnchor.x); // scale at which new box is to the left of old box
- float s2 = (blocking.x2 - box.x1) / (anchor.x - blockingAnchor.x); // scale at which new box is to the right of old box
- float s3 = (blocking.y1 - box.y2) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the top of old box
- float s4 = (blocking.y2 - box.y1) * yStretch / (anchor.y - blockingAnchor.y); // scale at which new box is to the bottom of old box
-
- if (std::isnan(s1) || std::isnan(s2)) s1 = s2 = 1;
- if (std::isnan(s3) || std::isnan(s4)) s3 = s4 = 1;
-
- 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 > box.maxScale) {
- // 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 = box.maxScale;
- }
-
- 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, -infinity, 0, infinity, infinity),
- // right
- CollisionBox(Point<float>(util::EXTENT, 0), 0, -infinity, 0, infinity, infinity),
- // top
- CollisionBox(Point<float>(0, 0), -infinity, 0, infinity, 0, infinity),
- // bottom
- CollisionBox(Point<float>(0, util::EXTENT), -infinity, 0, infinity, 0, infinity)
- }};
-
- float minPlacementScale = minScale;
-
- for (auto& box : feature.boxes) {
- const auto anchor = util::matrixMultiply(rotationMatrix, box.anchor);
-
- 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, 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,
- 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),
- box.maxScale);
-
- for (auto& blocking : edges) {
- minPlacementScale = util::max(minPlacementScale, findPlacementScale(box.anchor, rotatedBox, 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) {
- treeBoxes.emplace_back(getTreeBox(util::matrixMultiply(rotationMatrix, box.anchor), box), box, 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{
- CollisionPoint{
- anchor.x + box.x1 / scale,
- anchor.y + box.y1 / scale * yStretch
- },
- CollisionPoint{
- anchor.x + box.x2 / scale,
- anchor.y + box.y2 / scale * yStretch
- }
- };
-}
-
-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();
- };
-
- // Account for the rounding done when updating symbol shader variables.
- const float roundedScale = std::pow(2.0f, std::ceil(util::log2(scale) * 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.maxScale;
- };
-
- // 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 / scale;
- const int16_t y1 = anchor.y + collisionBox.y1 / scale * yStretch;
- const int16_t x2 = anchor.x + collisionBox.x2 / scale;
- const int16_t y2 = anchor.y + collisionBox.y2 / scale * 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 bdacbb7437..0000000000
--- a/src/mbgl/text/collision_tile.hpp
+++ /dev/null
@@ -1,67 +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"
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#pragma GCC diagnostic ignored "-Wmisleading-indentation"
-#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;
-
- const float minScale = 0.5f;
- const 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 Point<float>& blockingAnchor, const CollisionBox& blocking);
- Box getTreeBox(const Point<float>& anchor, const CollisionBox& box, const float scale = 1.0);
-
- Tree tree;
- Tree ignoredTree;
-};
-
-} // 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..177615857f
--- /dev/null
+++ b/src/mbgl/text/cross_tile_symbol_index.cpp
@@ -0,0 +1,165 @@
+#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) {
+ 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) {
+
+ symbolInstance.crossTileID = thisTileSymbol.crossTileID;
+ break;
+ }
+ }
+ }
+}
+
+CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() {
+}
+
+void CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& coord, SymbolBucket& bucket, uint32_t& maxCrossTileID) {
+ if (bucket.bucketInstanceId) return;
+ bucket.bucketInstanceId = ++maxBucketInstanceId;
+
+ uint8_t minZoom = 25;
+ uint8_t maxZoom = 0;
+ for (auto& it : indexes) {
+ auto z = it.first;
+ minZoom = std::min(minZoom, z);
+ maxZoom = std::max(maxZoom, z);
+ }
+
+
+ // make all higher-res child tiles block duplicate labels in this tile
+ for (auto z = maxZoom; z > coord.overscaledZ; z--) {
+ auto zoomIndexes = indexes.find(z);
+ if (zoomIndexes != indexes.end()) {
+ for (auto& childIndex : zoomIndexes->second) {
+ if (!childIndex.second.coord.isChildOf(coord)) {
+ continue;
+ }
+ childIndex.second.findMatches(bucket.symbolInstances, coord);
+ }
+ }
+ if (z == 0) {
+ break;
+ }
+ }
+
+ // make this tile block duplicate labels in lower-res parent tiles
+ for (auto z = coord.overscaledZ; z >= minZoom; z--) {
+ auto parentCoord = coord.scaledTo(z);
+ auto zoomIndexes = indexes.find(z);
+ if (zoomIndexes != indexes.end()) {
+ auto parentIndex = zoomIndexes->second.find(parentCoord);
+ if (parentIndex != zoomIndexes->second.end()) {
+ parentIndex->second.findMatches(bucket.symbolInstances, coord);
+ }
+ }
+ if (z == 0) {
+ break;
+ }
+ }
+
+ for (auto& symbolInstance : bucket.symbolInstances) {
+ if (!symbolInstance.crossTileID) {
+ // symbol did not match any known symbol, assign a new id
+ symbolInstance.crossTileID = ++maxCrossTileID;
+ }
+ }
+
+ indexes[coord.overscaledZ].emplace(coord, TileLayerIndex(coord, bucket.symbolInstances, bucket.bucketInstanceId));
+}
+
+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)) {
+ it = zoomIndexes.second.erase(it);
+ tilesChanged = true;
+ } else {
+ ++it;
+ }
+ }
+ }
+ return tilesChanged;
+}
+
+CrossTileSymbolIndex::CrossTileSymbolIndex() {}
+
+bool CrossTileSymbolIndex::addLayer(RenderSymbolLayer& symbolLayer) {
+
+ auto& layerIndex = layerIndexes[symbolLayer.getID()];
+
+ bool symbolBucketsChanged = false;
+ std::unordered_set<uint32_t> currentBucketIDs;
+
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ if (!symbolBucket.bucketInstanceId) {
+ symbolBucketsChanged = true;
+ }
+ layerIndex.addBucket(renderTile.tile.id, symbolBucket, maxCrossTileID);
+ currentBucketIDs.insert(symbolBucket.bucketInstanceId);
+ }
+
+ if (layerIndex.removeStaleBuckets(currentBucketIDs)) {
+ symbolBucketsChanged = true;
+ }
+ return symbolBucketsChanged;
+}
+
+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..c67cd37e00
--- /dev/null
+++ b/src/mbgl/text/cross_tile_symbol_index.hpp
@@ -0,0 +1,64 @@
+#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 <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&);
+
+ OverscaledTileID coord;
+ uint32_t bucketInstanceId;
+ std::map<std::u16string,std::vector<IndexedSymbolInstance>> indexedSymbolInstances;
+};
+
+class CrossTileSymbolLayerIndex {
+public:
+ CrossTileSymbolLayerIndex();
+ void addBucket(const OverscaledTileID&, SymbolBucket&, uint32_t& maxCrossTileID);
+ bool removeStaleBuckets(const std::unordered_set<uint32_t>& currentIDs);
+private:
+ std::map<uint8_t,std::map<OverscaledTileID,TileLayerIndex>> indexes;
+ uint32_t maxBucketInstanceId = 0;
+};
+
+class CrossTileSymbolIndex {
+public:
+ CrossTileSymbolIndex();
+
+ bool addLayer(RenderSymbolLayer&);
+
+ void reset();
+private:
+ std::map<std::string, CrossTileSymbolLayerIndex> layerIndexes;
+ uint32_t maxCrossTileID = 0;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/glyph.hpp b/src/mbgl/text/glyph.hpp
index b9eaedd302..08ff82a20a 100644
--- a/src/mbgl/text/glyph.hpp
+++ b/src/mbgl/text/glyph.hpp
@@ -7,6 +7,7 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/util/image.hpp>
+#include <mbgl/util/util.hpp>
#include <cstdint>
#include <vector>
@@ -57,13 +58,13 @@ using GlyphMap = std::map<FontStack, Glyphs>;
class PositionedGlyph {
public:
- explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, float angle_)
- : glyph(glyph_), x(x_), y(y_), angle(angle_) {}
+ explicit PositionedGlyph(GlyphID glyph_, float x_, float y_, bool vertical_)
+ : glyph(glyph_), x(x_), y(y_), vertical(vertical_) {}
GlyphID glyph = 0;
float x = 0;
float y = 0;
- float angle = 0;
+ bool vertical = false;
};
enum class WritingModeType : uint8_t;
@@ -74,10 +75,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(); }
@@ -89,23 +90,23 @@ enum class WritingModeType : uint8_t {
Vertical = 1 << 1,
};
-constexpr WritingModeType operator|(WritingModeType a, WritingModeType b) {
+MBGL_CONSTEXPR WritingModeType operator|(WritingModeType a, WritingModeType b) {
return WritingModeType(mbgl::underlying_type(a) | mbgl::underlying_type(b));
}
-constexpr WritingModeType& operator|=(WritingModeType& a, WritingModeType b) {
+MBGL_CONSTEXPR WritingModeType& operator|=(WritingModeType& a, WritingModeType b) {
return (a = a | b);
}
-constexpr bool operator&(WritingModeType lhs, WritingModeType rhs) {
+MBGL_CONSTEXPR bool operator&(WritingModeType lhs, WritingModeType rhs) {
return mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs);
}
-constexpr WritingModeType& operator&=(WritingModeType& lhs, WritingModeType rhs) {
+MBGL_CONSTEXPR WritingModeType& operator&=(WritingModeType& lhs, WritingModeType rhs) {
return (lhs = WritingModeType(mbgl::underlying_type(lhs) & mbgl::underlying_type(rhs)));
}
-constexpr WritingModeType operator~(WritingModeType value) {
+MBGL_CONSTEXPR WritingModeType operator~(WritingModeType value) {
return WritingModeType(~mbgl::underlying_type(value));
}
diff --git a/src/mbgl/text/glyph_manager.cpp b/src/mbgl/text/glyph_manager.cpp
index 916d39ae62..3130418908 100644
--- a/src/mbgl/text/glyph_manager.cpp
+++ b/src/mbgl/text/glyph_manager.cpp
@@ -4,14 +4,16 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
+#include <mbgl/util/tiny_sdf.hpp>
namespace mbgl {
static GlyphManagerObserver nullObserver;
-GlyphManager::GlyphManager(FileSource& fileSource_)
+GlyphManager::GlyphManager(FileSource& fileSource_, std::unique_ptr<LocalGlyphRasterizer> localGlyphRasterizer_)
: fileSource(fileSource_),
- observer(&nullObserver) {
+ observer(&nullObserver),
+ localGlyphRasterizer(std::move(localGlyphRasterizer_)) {
}
GlyphManager::~GlyphManager() = default;
@@ -30,14 +32,21 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD
const GlyphIDs& glyphIDs = dependency.second;
GlyphRangeSet ranges;
for (const auto& glyphID : glyphIDs) {
- ranges.insert(getGlyphRange(glyphID));
+ if (localGlyphRasterizer->canRasterizeGlyph(fontStack, glyphID)) {
+ if (entry.glyphs.find(glyphID) == entry.glyphs.end()) {
+ entry.glyphs.emplace(glyphID, makeMutable<Glyph>(generateLocalSDF(fontStack, glyphID)));
+ }
+ } else {
+ ranges.insert(getGlyphRange(glyphID));
+ }
}
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);
}
}
}
@@ -49,18 +58,20 @@ void GlyphManager::getGlyphs(GlyphRequestor& requestor, GlyphDependencies glyphD
}
}
-GlyphManager::GlyphRequest& GlyphManager::requestRange(Entry& entry, const FontStack& fontStack, const GlyphRange& range) {
- GlyphRequest& request = entry.ranges[range];
+Glyph GlyphManager::generateLocalSDF(const FontStack& fontStack, GlyphID glyphID) {
+ Glyph local = localGlyphRasterizer->rasterizeGlyph(fontStack, glyphID);
+ local.bitmap = util::transformRasterToSDF(local.bitmap, 8, .25);
+ return local;
+}
+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 00df079462..84db2c4be5 100644
--- a/src/mbgl/text/glyph_manager.hpp
+++ b/src/mbgl/text/glyph_manager.hpp
@@ -3,6 +3,7 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/text/glyph_manager_observer.hpp>
#include <mbgl/text/glyph_range.hpp>
+#include <mbgl/text/local_glyph_rasterizer.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/font_stack.hpp>
#include <mbgl/util/immutable.hpp>
@@ -24,7 +25,7 @@ public:
class GlyphManager : public util::noncopyable {
public:
- GlyphManager(FileSource&);
+ GlyphManager(FileSource&, std::unique_ptr<LocalGlyphRasterizer> = std::make_unique<LocalGlyphRasterizer>(optional<std::string>()));
~GlyphManager();
// Workers send a `getGlyphs` message to the main thread once they have determined
@@ -42,6 +43,8 @@ public:
void setObserver(GlyphManagerObserver*);
private:
+ Glyph generateLocalSDF(const FontStack& fontStack, GlyphID glyphID);
+
FileSource& fileSource;
std::string glyphURL;
@@ -58,11 +61,13 @@ 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&);
-
+
GlyphManagerObserver* observer = nullptr;
+
+ std::unique_ptr<LocalGlyphRasterizer> localGlyphRasterizer;
};
} // namespace mbgl
diff --git a/src/mbgl/text/local_glyph_rasterizer.hpp b/src/mbgl/text/local_glyph_rasterizer.hpp
new file mode 100644
index 0000000000..82b16b534d
--- /dev/null
+++ b/src/mbgl/text/local_glyph_rasterizer.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <mbgl/text/glyph.hpp>
+
+namespace mbgl {
+
+/*
+ Given a font stack and a glyph ID, platform-specific implementations of
+ LocalGlyphRasterizer will decide which, if any, local fonts to use and
+ then generate a matching glyph object with a greyscale rasterization of
+ the glyph and appropriate metrics. GlyphManager will then use TinySDF to
+ transform the rasterized bitmap into an SDF.
+
+ The JS equivalent of this functionality will only generate glyphs in the
+ 'CJK Unified Ideographs' and 'Hangul Syllables' ranges, for which it can
+ get away with rendering a fixed 30px square image and GlyphMetrics of:
+
+ width: 24,
+ height: 24,
+ left: 0,
+ top: -8,
+ advance: 24
+
+ The JS equivalent also uses heuristic evaluation of the font stack name
+ to control the font-weight it uses during rasterization.
+
+ It is left to platform-specific implementation to decide how best to
+ map a FontStack to a particular rasterization.
+
+ The default implementation simply refuses to rasterize any glyphs.
+*/
+
+class LocalGlyphRasterizer {
+public:
+ virtual ~LocalGlyphRasterizer();
+ LocalGlyphRasterizer(const optional<std::string> fontFamily = optional<std::string>());
+
+ // virtual so that test harness can override platform-specific behavior
+ virtual bool canRasterizeGlyph(const FontStack&, GlyphID);
+ virtual Glyph rasterizeGlyph(const FontStack&, GlyphID);
+private:
+ class Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp
new file mode 100644
index 0000000000..334748dda6
--- /dev/null
+++ b/src/mbgl/text/placement.cpp
@@ -0,0 +1,332 @@
+#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 offscreen)
+ : opacity((offscreen && 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 placedIcon, bool placedText, bool offscreen) :
+ icon(OpacityState(placedIcon, offscreen)),
+ text(OpacityState(placedText, offscreen)) {}
+
+JointOpacityState::JointOpacityState(const JointOpacityState& prevOpacityState, float increment, bool placedIcon, bool placedText) :
+ icon(OpacityState(prevOpacityState.icon, increment, placedIcon)),
+ text(OpacityState(prevOpacityState.text, increment, placedText)) {}
+
+bool JointOpacityState::isHidden() const {
+ return icon.isHidden() && text.isHidden();
+}
+
+Placement::Placement(const TransformState& state_, MapMode mapMode_)
+ : collisionIndex(state_)
+ , state(state_)
+ , mapMode(mapMode_)
+ , recentUntil(TimePoint::min())
+{}
+
+void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatrix, bool showCollisionBoxes) {
+
+ std::unordered_set<uint32_t> seenCrossTileIDs;
+
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ auto& layout = symbolBucket.layout;
+
+ const float pixelsToTileUnits = renderTile.id.pixelsToTileUnits(1, state.getZoom());
+
+ const float scale = std::pow(2, state.getZoom() - renderTile.tile.id.overscaledZ);
+ const float textPixelRatio = util::EXTENT / (util::tileSize * renderTile.tile.id.overscaleFactor());
+
+ mat4 posMatrix;
+ state.matrixFor(posMatrix, renderTile.id);
+ matrix::multiply(posMatrix, projMatrix, posMatrix);
+
+ mat4 textLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
+ layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<style::TextRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
+
+ mat4 iconLabelPlaneMatrix = getLabelPlaneMatrix(posMatrix,
+ layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map,
+ layout.get<style::IconRotationAlignment>() == style::AlignmentType::Map,
+ state,
+ pixelsToTileUnits);
+
+ placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs, renderTile.tile.holdForFade());
+ }
+}
+
+void Placement::placeLayerBucket(
+ SymbolBucket& bucket,
+ const mat4& posMatrix,
+ const mat4& textLabelPlaneMatrix,
+ const mat4& iconLabelPlaneMatrix,
+ const float scale,
+ const float textPixelRatio,
+ const bool showCollisionBoxes,
+ std::unordered_set<uint32_t>& seenCrossTileIDs,
+ const bool holdingForFade) {
+
+ auto partiallyEvaluatedTextSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom());
+ auto partiallyEvaluatedIconSize = bucket.iconSizeBinder->evaluateForZoom(state.getZoom());
+
+ const bool iconWithoutText = !bucket.hasTextData() || bucket.layout.get<style::TextOptional>();
+ const bool textWithoutIcon = !bucket.hasIconData() || bucket.layout.get<style::IconOptional>();
+
+ for (auto& symbolInstance : bucket.symbolInstances) {
+
+ if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) {
+ if (holdingForFade) {
+ // Mark all symbols from this tile as "not placed", but don't add to seenCrossTileIDs, because we don't
+ // know yet if we have a duplicate in a parent tile that _should_ be placed.
+ placements.emplace(symbolInstance.crossTileID, JointPlacement(false, false, false));
+ continue;
+ }
+
+ bool placeText = false;
+ bool placeIcon = false;
+ bool offscreen = true;
+
+ if (symbolInstance.placedTextIndex) {
+ PlacedSymbol& placedSymbol = bucket.text.placedSymbols.at(*symbolInstance.placedTextIndex);
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedTextSize, placedSymbol);
+
+ auto placed = collisionIndex.placeFeature(symbolInstance.textCollisionFeature,
+ posMatrix, textLabelPlaneMatrix, textPixelRatio,
+ placedSymbol, scale, fontSize,
+ bucket.layout.get<style::TextAllowOverlap>(),
+ bucket.layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes);
+ placeText = placed.first;
+ offscreen &= placed.second;
+ }
+
+ if (symbolInstance.placedIconIndex) {
+ PlacedSymbol& placedSymbol = bucket.icon.placedSymbols.at(*symbolInstance.placedIconIndex);
+ const float fontSize = evaluateSizeForFeature(partiallyEvaluatedIconSize, placedSymbol);
+
+ auto placed = collisionIndex.placeFeature(symbolInstance.iconCollisionFeature,
+ posMatrix, iconLabelPlaneMatrix, textPixelRatio,
+ placedSymbol, scale, fontSize,
+ bucket.layout.get<style::IconAllowOverlap>(),
+ bucket.layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map,
+ showCollisionBoxes);
+ placeIcon = placed.first;
+ offscreen &= placed.second;
+ }
+
+ // combine placements for icon and text
+ if (!iconWithoutText && !textWithoutIcon) {
+ placeText = placeIcon = placeText && placeIcon;
+ } else if (!textWithoutIcon) {
+ placeText = placeText && placeIcon;
+ } else if (!iconWithoutText) {
+ placeIcon = placeText && placeIcon;
+ }
+
+ if (placeText) {
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>());
+ }
+
+ if (placeIcon) {
+ collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>());
+ }
+
+ assert(symbolInstance.crossTileID != 0);
+
+ if (placements.find(symbolInstance.crossTileID) != placements.end()) {
+ // If there's a previous placement with this ID, it comes from a tile that's fading out
+ // Erase it so that the placement result from the non-fading tile supersedes it
+ placements.erase(symbolInstance.crossTileID);
+ }
+
+ placements.emplace(symbolInstance.crossTileID, JointPlacement(placeText, placeIcon, offscreen));
+ seenCrossTileIDs.insert(symbolInstance.crossTileID);
+ }
+ }
+}
+
+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.icon, jointPlacement.second.text));
+ placementChanged = placementChanged ||
+ jointPlacement.second.icon != prevOpacity->second.icon.placed ||
+ jointPlacement.second.text != prevOpacity->second.text.placed;
+ } else {
+ opacities.emplace(jointPlacement.first, JointOpacityState(jointPlacement.second.icon, jointPlacement.second.text, jointPlacement.second.offscreen));
+ placementChanged = placementChanged || jointPlacement.second.icon || jointPlacement.second.text;
+ }
+ }
+
+ // copy and update values from the previous placement that aren't in the current placement but haven't finished fading
+ for (auto& prevOpacity : prevPlacement.opacities) {
+ if (opacities.find(prevOpacity.first) == opacities.end()) {
+ JointOpacityState jointOpacity(prevOpacity.second, increment, false, false);
+ if (!jointOpacity.isHidden()) {
+ opacities.emplace(prevOpacity.first, jointOpacity);
+ placementChanged = placementChanged || prevOpacity.second.icon.placed || prevOpacity.second.text.placed;
+ }
+ }
+ }
+
+ return placementChanged;
+}
+
+void Placement::updateLayerOpacities(RenderSymbolLayer& symbolLayer) {
+ std::set<uint32_t> seenCrossTileIDs;
+ for (RenderTile& renderTile : symbolLayer.renderTiles) {
+ if (!renderTile.tile.isRenderable()) {
+ continue;
+ }
+
+ auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+ updateBucketOpacities(symbolBucket, seenCrossTileIDs);
+ }
+}
+
+void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>& seenCrossTileIDs) {
+ if (bucket.hasTextData()) bucket.text.opacityVertices.clear();
+ if (bucket.hasIconData()) bucket.icon.opacityVertices.clear();
+ if (bucket.hasCollisionBoxData()) bucket.collisionBox.dynamicVertices.clear();
+ if (bucket.hasCollisionCircleData()) bucket.collisionCircle.dynamicVertices.clear();
+
+ for (SymbolInstance& symbolInstance : bucket.symbolInstances) {
+ auto opacityState = seenCrossTileIDs.count(symbolInstance.crossTileID) == 0 ?
+ getOpacity(symbolInstance.crossTileID) :
+ JointOpacityState(false, false, false);
+
+ 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());
+}
+
+JointOpacityState Placement::getOpacity(uint32_t crossTileSymbolID) const {
+ auto it = opacities.find(crossTileSymbolID);
+ if (it != opacities.end()) {
+ return it->second;
+ } else {
+ return JointOpacityState(false, false, false);
+ }
+
+}
+
+float Placement::symbolFadeChange(TimePoint now) const {
+ if (mapMode == MapMode::Continuous) {
+ return std::chrono::duration<float>(now - commitTime) / Duration(std::chrono::milliseconds(300));
+ } else {
+ return 1.0;
+ }
+}
+
+bool Placement::hasTransitions(TimePoint now) const {
+ return symbolFadeChange(now) < 1.0 || stale;
+}
+
+bool Placement::stillRecent(TimePoint now) const {
+ return mapMode == MapMode::Continuous && recentUntil > now;
+}
+void Placement::setRecent(TimePoint now) {
+ stale = false;
+ if (mapMode == MapMode::Continuous) {
+ // Only set in continuous mode because "now" isn't defined in still mode
+ recentUntil = now + Duration(std::chrono::milliseconds(300));
+ }
+}
+
+void Placement::setStale() {
+ stale = true;
+}
+
+const CollisionIndex& Placement::getCollisionIndex() const {
+ return collisionIndex;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
new file mode 100644
index 0000000000..bcc20f15a4
--- /dev/null
+++ b/src/mbgl/text/placement.hpp
@@ -0,0 +1,91 @@
+#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 offscreen);
+ OpacityState(const OpacityState& prevOpacityState, float increment, bool placed);
+ bool isHidden() const;
+ float opacity;
+ bool placed;
+};
+
+class JointOpacityState {
+public:
+ JointOpacityState(bool placedIcon, bool placedText, bool offscreen);
+ 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 offscreen_)
+ : text(text_), icon(icon_), offscreen(offscreen_)
+ {}
+
+ const bool text;
+ const bool icon;
+ // offscreen = 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 offscreen;
+};
+
+class Placement {
+public:
+ Placement(const TransformState&, MapMode mapMode);
+ void placeLayer(RenderSymbolLayer&, const mat4&, bool showCollisionBoxes);
+ bool commit(const Placement& prevPlacement, TimePoint);
+ void updateLayerOpacities(RenderSymbolLayer&);
+ JointOpacityState getOpacity(uint32_t crossTileSymbolID) const;
+ float symbolFadeChange(TimePoint now) const;
+ bool hasTransitions(TimePoint now) const;
+
+ const CollisionIndex& getCollisionIndex() const;
+
+ bool stillRecent(TimePoint now) const;
+ void setRecent(TimePoint now);
+ void setStale();
+private:
+
+ void placeLayerBucket(
+ SymbolBucket&,
+ const mat4& posMatrix,
+ const mat4& textLabelPlaneMatrix,
+ const mat4& iconLabelPlaneMatrix,
+ const float scale,
+ const float pixelRatio,
+ const bool showCollisionBoxes,
+ std::unordered_set<uint32_t>& seenCrossTileIDs,
+ const bool holdingForFade);
+
+ void updateBucketOpacities(SymbolBucket&, std::set<uint32_t>&);
+
+ CollisionIndex collisionIndex;
+
+ TransformState state;
+ MapMode mapMode;
+ TimePoint commitTime;
+
+ std::unordered_map<uint32_t, JointPlacement> placements;
+ std::unordered_map<uint32_t, JointOpacityState> opacities;
+
+ TimePoint recentUntil;
+ bool stale = false;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement_config.hpp b/src/mbgl/text/placement_config.hpp
deleted file mode 100644
index 7e61cabc24..0000000000
--- a/src/mbgl/text/placement_config.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-namespace mbgl {
-
-class PlacementConfig {
-public:
- PlacementConfig(float angle_ = 0, float pitch_ = 0, bool debug_ = false)
- : angle(angle_), pitch(pitch_), debug(debug_) {
- }
-
- bool operator==(const PlacementConfig& rhs) const {
- return angle == rhs.angle && pitch == rhs.pitch && debug == rhs.debug;
- }
-
- bool operator!=(const PlacementConfig& rhs) const {
- return !operator==(rhs);
- }
-
-public:
- float angle;
- float pitch;
- bool debug;
-};
-
-} // namespace mbgl
diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp
index ab10c5a6b7..0014ae8d01 100644
--- a/src/mbgl/text/quads.cpp
+++ b/src/mbgl/text/quads.cpp
@@ -13,14 +13,9 @@ namespace mbgl {
using namespace style;
-const float globalMinScale = 0.5f; // underscale by 1 zoom level
-
-SymbolQuad getIconQuad(const Anchor& anchor,
- const PositionedIcon& shapedIcon,
- const GeometryCoordinates& line,
+SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
const SymbolLayoutProperties::Evaluated& layout,
const float layoutTextSize,
- const style::SymbolPlacementType placement,
const Shaping& shapedText) {
const ImagePosition& image = shapedIcon.image();
@@ -71,18 +66,7 @@ SymbolQuad getIconQuad(const Anchor& anchor,
bl = {left, bottom};
}
- float angle = shapedIcon.angle();
- if (placement == style::SymbolPlacementType::Line) {
- assert(static_cast<unsigned int>(anchor.segment) < line.size());
- const GeometryCoordinate &prev= line[anchor.segment];
- if (anchor.point.y == prev.y && anchor.point.x == prev.x &&
- static_cast<unsigned int>(anchor.segment + 1) < line.size()) {
- const GeometryCoordinate &next= line[anchor.segment + 1];
- angle += std::atan2(anchor.point.y - next.y, anchor.point.x - next.x) + M_PI;
- } else {
- angle += std::atan2(anchor.point.y - prev.y, anchor.point.x - prev.x);
- }
- }
+ const float angle = shapedIcon.angle();
if (angle) {
// Compute the transformation matrix.
@@ -104,212 +88,19 @@ SymbolQuad getIconQuad(const Anchor& anchor,
static_cast<uint16_t>(image.textureRect.h + border * 2)
};
- return SymbolQuad { tl, tr, bl, br, textureRect, 0, 0, anchor.point, globalMinScale, std::numeric_limits<float>::infinity(), shapedText.writingMode };
-}
-
-struct GlyphInstance {
- explicit GlyphInstance(Point<float> anchorPoint_) : anchorPoint(std::move(anchorPoint_)) {}
- explicit GlyphInstance(Point<float> anchorPoint_, bool upsideDown_, float minScale_, float maxScale_,
- float angle_)
- : anchorPoint(std::move(anchorPoint_)), upsideDown(upsideDown_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {}
-
- const Point<float> anchorPoint;
- const bool upsideDown = false;
- const float minScale = globalMinScale;
- const float maxScale = std::numeric_limits<float>::infinity();
- const float angle = 0.0f;
-};
-
-using GlyphInstances = std::vector<GlyphInstance>;
-
-struct VirtualSegment {
- Point<float> anchor;
- Point<float> end;
- size_t index;
- float minScale;
- float maxScale;
-};
-
-inline void insertSegmentGlyph(std::back_insert_iterator<GlyphInstances> glyphs,
- const VirtualSegment& virtualSegment,
- const bool glyphIsLogicallyForward,
- const bool upsideDown) {
- float segmentAngle = std::atan2(virtualSegment.end.y - virtualSegment.anchor.y, virtualSegment.end.x - virtualSegment.anchor.x);
- // If !glyphIsLogicallyForward, we're iterating through the segments in reverse logical order as well, so we need to flip the segment angle
- float glyphAngle = glyphIsLogicallyForward ? segmentAngle : segmentAngle + M_PI;
-
- // Insert a glyph rotated at this angle for display in the range from [scale, previous(larger) scale].
- glyphs = GlyphInstance{
- /* anchor */ virtualSegment.anchor,
- /* upsideDown */ upsideDown,
- /* minScale */ virtualSegment.minScale,
- /* maxScale */ virtualSegment.maxScale,
- /* angle */ static_cast<float>(std::fmod((glyphAngle + 2.0 * M_PI), (2.0 * M_PI)))};
-}
-
-/**
- Given the distance along the line from the label anchor to the beginning of the current segment,
- project a "virtual anchor" point at the same distance along the line extending out from this segment.
-
- B <-- beginning of current segment
-* . . . . . . . *--------* E <-- end of current segment
-VA |
- / VA = "virtual segment anchor"
- /
- ---*-----`
- A = label anchor
-
- Distance _along line_ from A to B == straight-line distance from VA to B.
- */
-inline Point<float> getVirtualSegmentAnchor(const Point<float>& segmentBegin, const Point<float>& segmentEnd, float distanceFromAnchorToSegmentBegin) {
- Point<float> segmentDirectionUnitVector = util::normal<float>(segmentBegin, segmentEnd);
- return segmentBegin - (segmentDirectionUnitVector * distanceFromAnchorToSegmentBegin);
-}
-
-/*
- Given the segment joining `segmentAnchor` and `segmentEnd` and a desired offset
- `glyphDistanceFromAnchor` at which a glyph is to be placed, calculate the minimum
- "scale" at which the glyph will fall on the segment (i.e., not past the end)
-
- "Scale" here refers to the ratio between the *rendered* zoom level and the text-layout
- zoom level, which is 1 + (source tile's zoom level). `glyphDistanceFromAnchor`, although
- passed in units consistent with the text-layout zoom level, is based on text size. So
- when the tile is being rendered at z < text-layout zoom, the glyph's actual distance from
- the anchor is larger relative to the segment's length than at layout time:
-
-
- GLYPH
- z == layout-zoom, scale == 1: segmentAnchor *--------------^-------------* segmentEnd
- z == layout-zoom - 1, scale == 0.5: segmentAnchor *--------------^* segmentEnd
-
- <-------------->
- Anchor-to-glyph distance stays visually fixed,
- so it changes relative to the segment.
-*/
-inline float getMinScaleForSegment(const float glyphDistanceFromAnchor,
- const Point<float>& segmentAnchor,
- const Point<float>& segmentEnd) {
- const auto distanceFromAnchorToEnd = util::dist<float>(segmentAnchor, segmentEnd);
- return glyphDistanceFromAnchor / distanceFromAnchorToEnd;
-}
-
-inline Point<float> getSegmentEnd(const bool glyphIsLogicallyForward,
- const GeometryCoordinates& line,
- const size_t segmentIndex) {
- return convertPoint<float>(glyphIsLogicallyForward ? line[segmentIndex+1] : line[segmentIndex]);
-}
-
-optional<VirtualSegment> getNextVirtualSegment(const VirtualSegment& previousVirtualSegment,
- const GeometryCoordinates& line,
- const float glyphDistanceFromAnchor,
- const bool glyphIsLogicallyForward) {
- auto nextSegmentBegin = previousVirtualSegment.end;
-
- auto end = nextSegmentBegin;
- size_t index = previousVirtualSegment.index;
-
- // skip duplicate nodes
- while (end == nextSegmentBegin) {
- // look ahead by 2 points in the line because the segment index refers to the beginning
- // of the segment, and we need an endpoint too
- if (glyphIsLogicallyForward && (index + 2 < line.size())) {
- index += 1;
- } else if (!glyphIsLogicallyForward && index != 0) {
- index -= 1;
- } else {
- return {};
- }
-
- end = getSegmentEnd(glyphIsLogicallyForward, line, index);
- }
-
- const auto anchor = getVirtualSegmentAnchor(nextSegmentBegin, end,
- util::dist<float>(previousVirtualSegment.anchor,
- previousVirtualSegment.end));
- return VirtualSegment {
- anchor,
- end,
- index,
- getMinScaleForSegment(glyphDistanceFromAnchor, anchor, end),
- previousVirtualSegment.minScale
- };
-}
-
-/*
- Given (1) a glyph positioned relative to an anchor point and (2) a line to follow,
- calculates which segment of the line the glyph will fall on for each possible
- scale range, and for each range produces a "virtual" anchor point and an angle that will
- place the glyph on the right segment and rotated to the correct angle.
-
- Because one glyph quad is made ahead of time for each possible orientation, the
- symbol_sdf shader can quickly handle changing layout as we zoom in and out
-
- If the "keepUpright" property is set, we call getLineGlyphs twice (once upright and
- once "upside down"). This will generate two sets of glyphs following the line in opposite
- directions. Later, SymbolLayout::place will look at the glyphs and based on the placement
- angle determine if their original anchor was "upright" or not -- based on that, it throws
- away one set of glyphs or the other (this work has to be done in the CPU, but it's just a
- filter so it's fast)
- */
-void getLineGlyphs(std::back_insert_iterator<GlyphInstances> glyphs,
- Anchor& anchor,
- float glyphHorizontalOffsetFromAnchor,
- const GeometryCoordinates& line,
- size_t anchorSegment,
- bool upsideDown) {
- assert(line.size() > anchorSegment+1);
-
- // This is true if the glyph is "logically forward" of the anchor point, based on the ordering of line segments
- // The actual angle of the line is irrelevant
- // If "upsideDown" is set, everything is flipped
- const bool glyphIsLogicallyForward = (glyphHorizontalOffsetFromAnchor >= 0) ^ upsideDown;
- const float glyphDistanceFromAnchor = std::fabs(glyphHorizontalOffsetFromAnchor);
-
- const auto initialSegmentEnd = getSegmentEnd(glyphIsLogicallyForward, line, anchorSegment);
- VirtualSegment virtualSegment = {
- anchor.point,
- initialSegmentEnd,
- anchorSegment,
- getMinScaleForSegment(glyphDistanceFromAnchor, anchor.point, initialSegmentEnd),
- std::numeric_limits<float>::infinity()
- };
-
- while (true) {
- insertSegmentGlyph(glyphs,
- virtualSegment,
- glyphIsLogicallyForward,
- upsideDown);
-
- if (virtualSegment.minScale <= anchor.scale) {
- // No need to calculate below the scale where the label starts showing
- return;
- }
-
- optional<VirtualSegment> nextVirtualSegment = getNextVirtualSegment(virtualSegment,
- line,
- glyphDistanceFromAnchor,
- glyphIsLogicallyForward);
- if (!nextVirtualSegment) {
- // There are no more segments, so we can't fit this glyph on the line at a lower scale
- // This implies we can't show the label at all at lower scale, so we update the anchor's min scale
- anchor.scale = virtualSegment.minScale;
- return;
- } else {
- virtualSegment = *nextVirtualSegment;
- }
- }
-
+ return SymbolQuad { tl, tr, bl, br, textureRect, shapedText.writingMode, { 0.0f, 0.0f } };
}
-SymbolQuads getGlyphQuads(Anchor& anchor,
- const Shaping& shapedText,
- const float boxScale,
- const GeometryCoordinates& line,
+SymbolQuads getGlyphQuads(const Shaping& shapedText,
const SymbolLayoutProperties::Evaluated& layout,
const style::SymbolPlacementType placement,
const GlyphPositionMap& positions) {
const float textRotate = layout.get<TextRotate>() * util::DEG2RAD;
- const bool keepUpright = layout.get<TextKeepUpright>();
+
+ const float oneEm = 24.0;
+ std::array<float, 2> textOffset = layout.get<TextOffset>();
+ textOffset[0] *= oneEm;
+ textOffset[1] *= oneEm;
SymbolQuads quads;
@@ -320,67 +111,64 @@ SymbolQuads getGlyphQuads(Anchor& anchor,
const GlyphPosition& glyph = positionsIt->second;
const Rect<uint16_t>& rect = glyph.rect;
- const float centerX = (positionedGlyph.x + glyph.metrics.advance / 2.0f) * boxScale;
-
- GlyphInstances glyphInstances;
- if (placement == style::SymbolPlacementType::Line) {
- getLineGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, false);
- if (keepUpright)
- getLineGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, true);
- } else {
- glyphInstances.emplace_back(GlyphInstance{anchor.point});
- }
// The rects have an addditional buffer that is not included in their size;
const float glyphPadding = 1.0f;
const float rectBuffer = 3.0f + glyphPadding;
- const float x1 = positionedGlyph.x + glyph.metrics.left - rectBuffer;
- const float y1 = positionedGlyph.y - glyph.metrics.top - rectBuffer;
- const float x2 = x1 + rect.w;
- const float y2 = y1 + rect.h;
+ const float halfAdvance = glyph.metrics.advance / 2.0;
+ const bool alongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map && placement == SymbolPlacementType::Line;
- const Point<float> center{positionedGlyph.x, static_cast<float>(static_cast<float>(glyph.metrics.advance) / 2.0)};
+ const Point<float> glyphOffset = alongLine ?
+ Point<float>{ positionedGlyph.x + halfAdvance, positionedGlyph.y } :
+ Point<float>{ 0.0f, 0.0f };
- Point<float> otl{x1, y1};
- Point<float> otr{x2, y1};
- Point<float> obl{x1, y2};
- Point<float> obr{x2, y2};
-
- if (positionedGlyph.angle != 0) {
- otl = util::rotate(otl - center, positionedGlyph.angle) + center;
- otr = util::rotate(otr - center, positionedGlyph.angle) + center;
- obl = util::rotate(obl - center, positionedGlyph.angle) + center;
- obr = util::rotate(obr - center, positionedGlyph.angle) + center;
- }
+ const Point<float> builtInOffset = alongLine ?
+ Point<float>{ 0.0f, 0.0f } :
+ Point<float>{ positionedGlyph.x + halfAdvance + textOffset[0], positionedGlyph.y + textOffset[1] };
- for (const GlyphInstance &instance : glyphInstances) {
- Point<float> tl = otl;
- Point<float> tr = otr;
- Point<float> bl = obl;
- Point<float> br = obr;
- if (textRotate) {
- // Compute the transformation matrix.
- float angle_sin = std::sin(textRotate);
- float angle_cos = std::cos(textRotate);
- std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
+ const float x1 = glyph.metrics.left - rectBuffer - halfAdvance + builtInOffset.x;
+ const float y1 = -glyph.metrics.top - rectBuffer + builtInOffset.y;
+ const float x2 = x1 + rect.w;
+ const float y2 = y1 + rect.h;
- tl = util::matrixMultiply(matrix, tl);
- tr = util::matrixMultiply(matrix, tr);
- bl = util::matrixMultiply(matrix, bl);
- br = util::matrixMultiply(matrix, br);
- }
+ Point<float> tl{x1, y1};
+ Point<float> tr{x2, y1};
+ Point<float> bl{x1, y2};
+ Point<float> br{x2, y2};
+
+ if (alongLine && positionedGlyph.vertical) {
+ // Vertical-supporting glyphs are laid out in 24x24 point boxes (1 square em)
+ // In horizontal orientation, the y values for glyphs are below the midline
+ // and we use a "yOffset" of -17 to pull them up to the middle.
+ // By rotating counter-clockwise around the point at the center of the left
+ // edge of a 24x24 layout box centered below the midline, we align the center
+ // of the glyphs with the horizontal midline, so the yOffset is no longer
+ // necessary, but we also pull the glyph to the left along the x axis
+ const Point<float> center{-halfAdvance, halfAdvance};
+ const float verticalRotation = -M_PI_2;
+ const Point<float> xOffsetCorrection{5, 0};
+
+ tl = util::rotate(tl - center, verticalRotation) + center + xOffsetCorrection;
+ tr = util::rotate(tr - center, verticalRotation) + center + xOffsetCorrection;
+ bl = util::rotate(bl - center, verticalRotation) + center + xOffsetCorrection;
+ br = util::rotate(br - center, verticalRotation) + center + xOffsetCorrection;
+ }
- // Prevent label from extending past the end of the line
- const float glyphMinScale = std::max(instance.minScale, anchor.scale);
+ if (textRotate) {
+ // Compute the transformation matrix.
+ float angle_sin = std::sin(textRotate);
+ float angle_cos = std::cos(textRotate);
+ std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
- // All the glyphs for a label are tagged with either the "right side up" or "upside down" anchor angle,
- // which is used at placement time to determine which set to show
- const float anchorAngle = std::fmod((anchor.angle + (instance.upsideDown ? M_PI : 0.0) + 2 * M_PI), (2 * M_PI));
- const float glyphAngle = std::fmod((instance.angle + (instance.upsideDown ? M_PI : 0.0) + 2 * M_PI), (2 * M_PI));
- quads.emplace_back(tl, tr, bl, br, rect, anchorAngle, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale, shapedText.writingMode);
+ tl = util::matrixMultiply(matrix, tl);
+ tr = util::matrixMultiply(matrix, tr);
+ bl = util::matrixMultiply(matrix, bl);
+ br = util::matrixMultiply(matrix, br);
}
+
+ quads.emplace_back(tl, tr, bl, br, rect, shapedText.writingMode, glyphOffset);
}
return quads;
diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp
index b29f6b0ad3..33d003c935 100644
--- a/src/mbgl/text/quads.hpp
+++ b/src/mbgl/text/quads.hpp
@@ -19,50 +19,33 @@ public:
Point<float> bl_,
Point<float> br_,
Rect<uint16_t> tex_,
- float anchorAngle_,
- float glyphAngle_,
- Point<float> anchorPoint_,
- float minScale_,
- float maxScale_,
- WritingModeType writingMode_)
+ WritingModeType writingMode_,
+ Point<float> glyphOffset_)
: tl(std::move(tl_)),
tr(std::move(tr_)),
bl(std::move(bl_)),
br(std::move(br_)),
tex(std::move(tex_)),
- anchorAngle(anchorAngle_),
- glyphAngle(glyphAngle_),
- anchorPoint(std::move(anchorPoint_)),
- minScale(minScale_),
- maxScale(maxScale_),
- writingMode(writingMode_) {}
+ writingMode(writingMode_),
+ glyphOffset(glyphOffset_) {}
Point<float> tl;
Point<float> tr;
Point<float> bl;
Point<float> br;
Rect<uint16_t> tex;
- float anchorAngle, glyphAngle;
- Point<float> anchorPoint;
- float minScale;
- float maxScale;
WritingModeType writingMode;
+ Point<float> glyphOffset;
};
using SymbolQuads = std::vector<SymbolQuad>;
-SymbolQuad getIconQuad(const Anchor& anchor,
- const PositionedIcon& shapedIcon,
- const GeometryCoordinates& line,
+SymbolQuad getIconQuad(const PositionedIcon& shapedIcon,
const style::SymbolLayoutProperties::Evaluated&,
const float layoutTextSize,
- style::SymbolPlacementType placement,
const Shaping& shapedText);
-SymbolQuads getGlyphQuads(Anchor& anchor,
- const Shaping& shapedText,
- const float boxScale,
- const GeometryCoordinates& line,
+SymbolQuads getGlyphQuads(const Shaping& shapedText,
const style::SymbolLayoutProperties::Evaluated&,
style::SymbolPlacementType placement,
const GlyphPositionMap& positions);
diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp
index 338abe2e43..a8232836b6 100644
--- a/src/mbgl/text/shaping.cpp
+++ b/src/mbgl/text/shaping.cpp
@@ -7,15 +7,67 @@
#include <boost/algorithm/string.hpp>
#include <algorithm>
+#include <cmath>
namespace mbgl {
-PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, const float iconRotation) {
+struct AnchorAlignment {
+ AnchorAlignment(float horizontal_, float vertical_)
+ : horizontalAlign(horizontal_), verticalAlign(vertical_) {
+ }
+
+ float horizontalAlign;
+ float verticalAlign;
+};
+
+AnchorAlignment getAnchorAlignment(style::SymbolAnchorType anchor) {
+ float horizontalAlign = 0.5;
+ float verticalAlign = 0.5;
+
+ switch (anchor) {
+ case style::SymbolAnchorType::Top:
+ case style::SymbolAnchorType::Bottom:
+ case style::SymbolAnchorType::Center:
+ break;
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::TopRight:
+ case style::SymbolAnchorType::BottomRight:
+ horizontalAlign = 1;
+ break;
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::BottomLeft:
+ horizontalAlign = 0;
+ break;
+ }
+
+ switch (anchor) {
+ case style::SymbolAnchorType::Left:
+ case style::SymbolAnchorType::Right:
+ case style::SymbolAnchorType::Center:
+ break;
+ case style::SymbolAnchorType::Bottom:
+ case style::SymbolAnchorType::BottomLeft:
+ case style::SymbolAnchorType::BottomRight:
+ verticalAlign = 1;
+ break;
+ case style::SymbolAnchorType::Top:
+ case style::SymbolAnchorType::TopLeft:
+ case style::SymbolAnchorType::TopRight:
+ verticalAlign = 0;
+ break;
+ }
+
+ return AnchorAlignment(horizontalAlign, verticalAlign);
+}
+
+PositionedIcon PositionedIcon::shapeIcon(const ImagePosition& image, const std::array<float, 2>& iconOffset, style::SymbolAnchorType iconAnchor, const float iconRotation) {
+ AnchorAlignment anchorAlign = getAnchorAlignment(iconAnchor);
float dx = iconOffset[0];
float dy = iconOffset[1];
- float x1 = dx - image.displaySize()[0] / 2.0f;
+ float x1 = dx - image.displaySize()[0] * anchorAlign.horizontalAlign;
float x2 = x1 + image.displaySize()[0];
- float y1 = dy - image.displaySize()[1] / 2.0f;
+ float y1 = dy - image.displaySize()[1] * anchorAlign.verticalAlign;
float y2 = y1 + image.displaySize()[1];
return PositionedIcon { image, y1, y2, x1, x2, iconRotation };
@@ -27,12 +79,9 @@ void align(Shaping& shaping,
const float verticalAlign,
const float maxLineLength,
const float lineHeight,
- const std::size_t lineCount,
- const Point<float>& translate) {
- const float shiftX =
- (justify - horizontalAlign) * maxLineLength + ::round(translate.x);
- const float shiftY =
- (-verticalAlign * lineCount + 0.5) * lineHeight + ::round(translate.y);
+ const std::size_t lineCount) {
+ const float shiftX = (justify - horizontalAlign) * maxLineLength;
+ const float shiftY = (-verticalAlign * lineCount + 0.5) * lineHeight;
for (auto& glyph : shaping.positionedGlyphs) {
glyph.x += shiftX;
@@ -75,7 +124,7 @@ float determineAverageLineWidth(const std::u16string& logicalInput,
}
}
- int32_t targetLineCount = std::fmax(1, std::ceil(totalWidth / maxWidth));
+ int32_t targetLineCount = ::fmax(1, std::ceil(totalWidth / maxWidth));
return totalWidth / targetLineCount;
}
@@ -202,10 +251,8 @@ void shapeLines(Shaping& shaping,
const std::vector<std::u16string>& lines,
const float spacing,
const float lineHeight,
- const float horizontalAlign,
- const float verticalAlign,
- const float justify,
- const Point<float>& translate,
+ const style::SymbolAnchorType textAnchor,
+ const style::TextJustifyType textJustify,
const float verticalHeight,
const WritingModeType writingMode,
const Glyphs& glyphs) {
@@ -217,6 +264,10 @@ void shapeLines(Shaping& shaping,
float y = yOffset;
float maxLineLength = 0;
+
+ const float justify = textJustify == style::TextJustifyType::Right ? 1 :
+ textJustify == style::TextJustifyType::Left ? 0 :
+ 0.5;
for (std::u16string line : lines) {
// Collapse whitespace so it doesn't throw off justification
@@ -237,10 +288,10 @@ void shapeLines(Shaping& shaping,
const Glyph& glyph = **it->second;
if (writingMode == WritingModeType::Horizontal || !util::i18n::hasUprightVerticalOrientation(chr)) {
- shaping.positionedGlyphs.emplace_back(chr, x, y, 0);
+ shaping.positionedGlyphs.emplace_back(chr, x, y, false);
x += glyph.metrics.advance + spacing;
} else {
- shaping.positionedGlyphs.emplace_back(chr, x, 0, -M_PI_2);
+ shaping.positionedGlyphs.emplace_back(chr, x, 0, true);
x += verticalHeight + spacing;
}
}
@@ -257,24 +308,25 @@ void shapeLines(Shaping& shaping,
x = 0;
y += lineHeight;
}
-
- align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight,
- lines.size(), translate);
- const uint32_t height = lines.size() * lineHeight;
-
+
+ auto anchorAlign = getAnchorAlignment(textAnchor);
+
+ align(shaping, justify, anchorAlign.horizontalAlign, anchorAlign.verticalAlign, maxLineLength,
+ lineHeight, lines.size());
+ const float height = lines.size() * lineHeight;
+
// Calculate the bounding box
- shaping.top += -verticalAlign * height;
+ shaping.top += -anchorAlign.verticalAlign * height;
shaping.bottom = shaping.top + height;
- shaping.left += -horizontalAlign * maxLineLength;
+ shaping.left += -anchorAlign.horizontalAlign * maxLineLength;
shaping.right = shaping.left + maxLineLength;
}
const Shaping getShaping(const std::u16string& logicalInput,
const float maxWidth,
const float lineHeight,
- const float horizontalAlign,
- const float verticalAlign,
- const float justify,
+ const style::SymbolAnchorType textAnchor,
+ const style::TextJustifyType textJustify,
const float spacing,
const Point<float>& translate,
const float verticalHeight,
@@ -287,8 +339,8 @@ const Shaping getShaping(const std::u16string& logicalInput,
bidi.processText(logicalInput,
determineLineBreaks(logicalInput, spacing, maxWidth, writingMode, glyphs));
- shapeLines(shaping, reorderedLines, spacing, lineHeight, horizontalAlign, verticalAlign,
- justify, translate, verticalHeight, writingMode, glyphs);
+ shapeLines(shaping, reorderedLines, spacing, lineHeight, textAnchor,
+ textJustify, verticalHeight, writingMode, glyphs);
return shaping;
}
diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp
index ca475e2a6c..0a961849e5 100644
--- a/src/mbgl/text/shaping.hpp
+++ b/src/mbgl/text/shaping.hpp
@@ -2,6 +2,7 @@
#include <mbgl/text/glyph.hpp>
#include <mbgl/renderer/image_atlas.hpp>
+#include <mbgl/style/types.hpp>
namespace mbgl {
@@ -31,7 +32,10 @@ private:
float _angle;
public:
- static PositionedIcon shapeIcon(const ImagePosition&, const std::array<float, 2>& iconOffset, const float iconRotation);
+ static PositionedIcon shapeIcon(const ImagePosition&,
+ const std::array<float, 2>& iconOffset,
+ style::SymbolAnchorType iconAnchor,
+ const float iconRotation);
const ImagePosition& image() const { return _image; }
float top() const { return _top; }
@@ -44,9 +48,8 @@ public:
const Shaping getShaping(const std::u16string& string,
float maxWidth,
float lineHeight,
- float horizontalAlign,
- float verticalAlign,
- float justify,
+ style::SymbolAnchorType textAnchor,
+ style::TextJustifyType textJustify,
float spacing,
const Point<float>& translate,
float verticalHeight,
diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp
new file mode 100644
index 0000000000..7c8a85c4a4
--- /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).features;
+ } else {
+ setNecessity(TileNecessity::Optional);
+ }
+ setData(std::make_unique<GeoJSONTileData>(std::move(featureData)));
+}
+
+void CustomGeometryTile::invalidateTileData() {
+ stale = true;
+ observer->onTileChanged(*this);
+}
+
+//Fetching tile data for custom sources is assumed to be an expensive operation.
+// Only required tiles make fetchTile requests. Attempt to cancel a tile
+// that is no longer required.
+void CustomGeometryTile::setNecessity(TileNecessity newNecessity) {
+ if (newNecessity != necessity || stale ) {
+ necessity = newNecessity;
+ if (necessity == TileNecessity::Required) {
+ loader.invoke(&style::CustomTileLoader::fetchTile, id, actorRef);
+ stale = false;
+ } else if (!isRenderable()) {
+ loader.invoke(&style::CustomTileLoader::cancelTile, id);
+ }
+ }
+}
+
+void CustomGeometryTile::querySourceFeatures(
+ std::vector<Feature>& result,
+ const SourceQueryOptions& queryOptions) {
+
+ // Ignore the sourceLayer, there is only one
+ auto layer = getData()->getLayer({});
+
+ if (layer) {
+ auto featureCount = layer->featureCount();
+ for (std::size_t i = 0; i < featureCount; i++) {
+ auto feature = layer->getFeature(i);
+
+ // Apply filter, if any
+ if (queryOptions.filter && !(*queryOptions.filter)(*feature)) {
+ continue;
+ }
+
+ result.push_back(convertFeature(*feature, id.canonical));
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/custom_geometry_tile.hpp b/src/mbgl/tile/custom_geometry_tile.hpp
new file mode 100644
index 0000000000..1df44e6b2a
--- /dev/null
+++ b/src/mbgl/tile/custom_geometry_tile.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/actor/mailbox.hpp>
+
+namespace mbgl {
+
+class TileParameters;
+
+namespace style {
+class CustomTileLoader;
+} // namespace style
+
+class CustomGeometryTile: public GeometryTile {
+public:
+ CustomGeometryTile(const OverscaledTileID&,
+ std::string sourceID,
+ const TileParameters&,
+ const style::CustomGeometrySource::TileOptions,
+ ActorRef<style::CustomTileLoader> loader);
+ ~CustomGeometryTile() override;
+
+ void setTileData(const GeoJSON& data);
+ void invalidateTileData();
+
+ void setNecessity(TileNecessity) final;
+
+ void querySourceFeatures(
+ std::vector<Feature>& result,
+ const SourceQueryOptions&) override;
+
+private:
+ bool stale = true;
+ TileNecessity necessity;
+ const style::CustomGeometrySource::TileOptions options;
+ ActorRef<style::CustomTileLoader> loader;
+ std::shared_ptr<Mailbox> mailbox;
+ ActorRef<CustomGeometryTile> actorRef;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index e9865e8272..bbec899950 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -1,102 +1,11 @@
#include <mbgl/tile/geojson_tile.hpp>
-#include <mbgl/tile/geometry_tile_data.hpp>
-#include <mbgl/map/query.hpp>
+#include <mbgl/tile/geojson_tile_data.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
-
-#include <mapbox/geojsonvt.hpp>
-#include <supercluster.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
namespace mbgl {
-// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have
-// one layer, and it is always returned regardless of which layer is requested.
-
-class GeoJSONTileFeature : public GeometryTileFeature {
-public:
- const mapbox::geometry::feature<int16_t>& feature;
-
- GeoJSONTileFeature(const mapbox::geometry::feature<int16_t>& 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 {
- GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry);
-
- // https://github.com/mapbox/geojson-vt-cpp/issues/44
- if (getType() == FeatureType::Polygon) {
- geometry = fixupPolygons(geometry);
- }
-
- return geometry;
- }
-
- optional<Value> getValue(const std::string& key) const override {
- auto it = feature.properties.find(key);
- if (it != feature.properties.end()) {
- return optional<Value>(it->second);
- }
- return optional<Value>();
- }
-};
-
-class GeoJSONTileLayer : public GeometryTileLayer {
-public:
- GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
- : features(std::move(features_)) {
- }
-
- std::size_t featureCount() const override {
- return features->size();
- }
-
- std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
- return std::make_unique<GeoJSONTileFeature>((*features)[i]);
- }
-
- std::string getName() const override {
- return "";
- }
-
-private:
- std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
-};
-
-class GeoJSONTileData : public GeometryTileData {
-public:
- GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_)
- : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>(
- std::move(features_))) {
- }
-
- GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
- : features(std::move(features_)) {
- }
-
- std::unique_ptr<GeometryTileData> clone() const override {
- return std::make_unique<GeoJSONTileData>(features);
- }
-
- std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override {
- return std::make_unique<GeoJSONTileLayer>(features);
- }
-
-
-private:
- std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
-};
-
GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
std::string sourceID_,
const TileParameters& parameters,
@@ -108,27 +17,25 @@ GeoJSONTile::GeoJSONTile(const OverscaledTileID& overscaledTileID,
void GeoJSONTile::updateData(mapbox::geometry::feature_collection<int16_t> features) {
setData(std::make_unique<GeoJSONTileData>(std::move(features)));
}
-
-void GeoJSONTile::setNecessity(Necessity) {}
void GeoJSONTile::querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions& options) {
// 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 (options.filter && !(*options.filter)(*feature)) {
- continue;
+ if (auto tileData = getData()) {
+ if (auto layer = tileData->getLayer({})) {
+ auto featureCount = layer->featureCount();
+ for (std::size_t i = 0; i < featureCount; i++) {
+ auto feature = layer->getFeature(i);
+
+ // Apply filter, if any
+ if (options.filter && !(*options.filter)(*feature)) {
+ continue;
+ }
+
+ result.push_back(convertFeature(*feature, id.canonical));
}
-
- result.push_back(convertFeature(*feature, id.canonical));
}
}
}
diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp
index d8a0a379d7..270406267c 100644
--- a/src/mbgl/tile/geojson_tile.hpp
+++ b/src/mbgl/tile/geojson_tile.hpp
@@ -15,8 +15,6 @@ public:
mapbox::geometry::feature_collection<int16_t>);
void updateData(mapbox::geometry::feature_collection<int16_t>);
-
- void setNecessity(Necessity) final;
void querySourceFeatures(
std::vector<Feature>& result,
diff --git a/src/mbgl/tile/geojson_tile_data.hpp b/src/mbgl/tile/geojson_tile_data.hpp
new file mode 100644
index 0000000000..3402c2a009
--- /dev/null
+++ b/src/mbgl/tile/geojson_tile_data.hpp
@@ -0,0 +1,94 @@
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+
+// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have
+// one layer, and it is always returned regardless of which layer is requested.
+
+class GeoJSONTileFeature : public GeometryTileFeature {
+public:
+ const mapbox::geometry::feature<int16_t>& feature;
+
+ GeoJSONTileFeature(const mapbox::geometry::feature<int16_t>& 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 {
+ GeometryCollection geometry = apply_visitor(ToGeometryCollection(), feature.geometry);
+
+ // https://github.com/mapbox/geojson-vt-cpp/issues/44
+ if (getType() == FeatureType::Polygon) {
+ geometry = fixupPolygons(geometry);
+ }
+
+ return geometry;
+ }
+
+ optional<Value> getValue(const std::string& key) const override {
+ auto it = feature.properties.find(key);
+ if (it != feature.properties.end()) {
+ return optional<Value>(it->second);
+ }
+ return optional<Value>();
+ }
+};
+
+class GeoJSONTileLayer : public GeometryTileLayer {
+public:
+ GeoJSONTileLayer(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
+ : features(std::move(features_)) {
+ }
+
+ std::size_t featureCount() const override {
+ return features->size();
+ }
+
+ std::unique_ptr<GeometryTileFeature> getFeature(std::size_t i) const override {
+ return std::make_unique<GeoJSONTileFeature>((*features)[i]);
+ }
+
+ std::string getName() const override {
+ return "";
+ }
+
+private:
+ std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
+};
+
+class GeoJSONTileData : public GeometryTileData {
+public:
+ GeoJSONTileData(mapbox::geometry::feature_collection<int16_t> features_)
+ : features(std::make_shared<mapbox::geometry::feature_collection<int16_t>>(
+ std::move(features_))) {
+ }
+
+ GeoJSONTileData(std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features_)
+ : features(std::move(features_)) {
+ }
+
+ std::unique_ptr<GeometryTileData> clone() const override {
+ return std::make_unique<GeoJSONTileData>(features);
+ }
+
+ std::unique_ptr<GeometryTileLayer> getLayer(const std::string&) const override {
+ return std::make_unique<GeoJSONTileLayer>(features);
+ }
+
+
+private:
+ std::shared_ptr<const mapbox::geometry::feature_collection<int16_t>> features;
+};
+
+} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index 4ab11d79fe..701e7cf2d6 100644
--- a/src/mbgl/tile/geometry_tile.cpp
+++ b/src/mbgl/tile/geometry_tile.cpp
@@ -10,17 +10,15 @@
#include <mbgl/renderer/layers/render_custom_layer.hpp>
#include <mbgl/renderer/layers/render_symbol_layer.hpp>
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/text/glyph_atlas.hpp>
#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/map/query.hpp>
-#include <mbgl/util/run_loop.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/util/chrono.hpp>
#include <mbgl/util/logging.hpp>
+#include <mbgl/actor/scheduler.hpp>
#include <iostream>
@@ -28,21 +26,40 @@ namespace mbgl {
using namespace style;
+/*
+ Correlation between GeometryTile and GeometryTileWorker is safeguarded by two
+ correlation schemes:
+
+ 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', 'setShowCollisionBoxes') occurs. This is important for
+ still mode rendering as we want to render only when all layout and placement
+ operations are completed.
+
+ GeometryTileWorker's 'imageCorrelationID' is used for checking whether an
+ image request reply coming from `GeometryTile` is valid. Previous image
+ request replies are ignored as they result in incomplete placement attempts
+ that could flag the tile as non-pending too early.
+ */
+
GeometryTile::GeometryTile(const OverscaledTileID& id_,
std::string sourceID_,
const TileParameters& parameters)
: Tile(id_),
sourceID(std::move(sourceID_)),
- mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
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),
- placementThrottler(Milliseconds(300), [this] { invokePlacement(); }) {
+ mode(parameters.mode),
+ showCollisionBoxes(parameters.debugOptions & MapDebugOptions::Collision) {
}
GeometryTile::~GeometryTile() {
@@ -61,7 +78,6 @@ void GeometryTile::markObsolete() {
void GeometryTile::setError(std::exception_ptr err) {
loaded = true;
- renderable = false;
observer->onTileError(*this, err);
}
@@ -74,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;
- placementThrottler.invoke();
-}
-
-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
@@ -119,37 +116,47 @@ void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers)
worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID);
}
-void GeometryTile::onLayout(LayoutResult result) {
- loaded = true;
- renderable = true;
+void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) {
+ if (showCollisionBoxes != showCollisionBoxes_) {
+ showCollisionBoxes = showCollisionBoxes_;
+ ++correlationID;
+ worker.invoke(&GeometryTileWorker::setShowCollisionBoxes, showCollisionBoxes, correlationID);
+ }
+}
+
+void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) {
+ // Don't mark ourselves loaded or renderable until the first successful placement
+ // TODO: Ideally we'd render this tile without symbols as long as this tile wasn't
+ // replacing a tile at a different zoom that _did_ have symbols.
+ (void)resultCorrelationID;
nonSymbolBuckets = std::move(result.nonSymbolBuckets);
featureIndex = std::move(result.featureIndex);
data = std::move(result.tileData);
- collisionTile.reset();
observer->onTileChanged(*this);
}
-void GeometryTile::onPlacement(PlacementResult result) {
+void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorrelationID) {
loaded = true;
renderable = true;
- if (result.correlationID == correlationID) {
+ if (resultCorrelationID == correlationID) {
pending = false;
}
symbolBuckets = std::move(result.symbolBuckets);
- collisionTile = std::move(result.collisionTile);
if (result.glyphAtlasImage) {
glyphAtlasImage = std::move(*result.glyphAtlasImage);
}
if (result.iconAtlasImage) {
iconAtlasImage = std::move(*result.iconAtlasImage);
}
+
observer->onTileChanged(*this);
}
-void GeometryTile::onError(std::exception_ptr err) {
+void GeometryTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) {
loaded = true;
- pending = false;
- renderable = false;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
observer->onTileError(*this, err);
}
@@ -161,27 +168,27 @@ void GeometryTile::getGlyphs(GlyphDependencies glyphDependencies) {
glyphManager.getGlyphs(*this, std::move(glyphDependencies));
}
-void GeometryTile::onImagesAvailable(ImageMap images) {
- worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images));
+void GeometryTile::onImagesAvailable(ImageMap images, uint64_t imageCorrelationID) {
+ worker.invoke(&GeometryTileWorker::onImagesAvailable, std::move(images), imageCorrelationID);
}
-void GeometryTile::getImages(ImageDependencies imageDependencies) {
- imageManager.getImages(*this, std::move(imageDependencies));
+void GeometryTile::getImages(ImageRequestPair pair) {
+ imageManager.getImages(*this, std::move(pair));
}
void GeometryTile::upload(gl::Context& context) {
- auto upload = [&] (Bucket& bucket) {
+ auto uploadFn = [&] (Bucket& bucket) {
if (bucket.needsUpload()) {
bucket.upload(context);
}
};
for (auto& entry : nonSymbolBuckets) {
- upload(*entry.second);
+ uploadFn(*entry.second);
}
for (auto& entry : symbolBuckets) {
- upload(*entry.second);
+ uploadFn(*entry.second);
}
if (glyphAtlasImage) {
@@ -210,11 +217,21 @@ void GeometryTile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState& transformState,
- const RenderStyle& style,
- const RenderedQueryOptions& options) {
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) {
if (!featureIndex || !data) return;
+ // Determine the additional radius needed factoring in property functions
+ float additionalRadius = 0;
+ for (const RenderLayer* layer : layers) {
+ auto bucket = getBucket(*layer->baseImpl);
+ if (bucket) {
+ additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer));
+ }
+ }
+
featureIndex->query(result,
queryGeometry,
transformState.getAngle(),
@@ -222,10 +239,11 @@ void GeometryTile::queryRenderedFeatures(
std::pow(2, transformState.getZoom() - id.overscaledZ),
options,
*data,
- id.canonical,
- style,
- collisionTile.get(),
- *this);
+ id.toUnwrapped(),
+ sourceID,
+ layers,
+ collisionIndex,
+ additionalRadius);
}
void GeometryTile::querySourceFeatures(
@@ -264,4 +282,37 @@ void GeometryTile::querySourceFeatures(
}
}
+void GeometryTile::resetCrossTileIDs() {
+ for (auto& bucket : symbolBuckets) {
+ auto symbolBucket = dynamic_cast<SymbolBucket*>(bucket.second.get());
+ if (symbolBucket && symbolBucket->bucketInstanceId) {
+ symbolBucket->bucketInstanceId = 0;
+ for (auto& symbolInstance : symbolBucket->symbolInstances) {
+ symbolInstance.crossTileID = 0;
+ }
+ }
+ }
+}
+
+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 77202d20b6..6f871819a4 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -4,10 +4,10 @@
#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/util/feature.hpp>
#include <mbgl/util/throttler.hpp>
#include <mbgl/actor/actor.hpp>
+#include <mbgl/geometry/feature_index.hpp>
#include <atomic>
#include <memory>
@@ -17,9 +17,6 @@
namespace mbgl {
class GeometryTileData;
-class FeatureIndex;
-class CollisionTile;
-class RenderStyle;
class RenderLayer;
class SourceQueryOptions;
class TileParameters;
@@ -37,14 +34,14 @@ 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) override;
+ void onImagesAvailable(ImageMap, uint64_t imageCorrelationID) override;
void getGlyphs(GlyphDependencies);
- void getImages(ImageDependencies);
+ void getImages(ImageRequestPair);
void upload(gl::Context&) override;
Bucket* getBucket(const style::Layer::Impl&) const override;
@@ -56,8 +53,9 @@ public:
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions& options) override;
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options,
+ const CollisionIndex& collisionIndex) override;
void querySourceFeatures(
std::vector<Feature>& result,
@@ -70,21 +68,39 @@ public:
std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
std::unique_ptr<FeatureIndex> featureIndex;
std::unique_ptr<GeometryTileData> tileData;
- uint64_t correlationID;
+
+ 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);
+ 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;
- uint64_t correlationID;
+
+ PlacementResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets_,
+ optional<AlphaImage> glyphAtlasImage_,
+ optional<PremultipliedImage> iconAtlasImage_)
+ : symbolBuckets(std::move(symbolBuckets_)),
+ glyphAtlasImage(std::move(glyphAtlasImage_)),
+ iconAtlasImage(std::move(iconAtlasImage_)) {}
};
- void onPlacement(PlacementResult);
+ void onPlacement(PlacementResult, uint64_t correlationID);
- void onError(std::exception_ptr);
+ void onError(std::exception_ptr, uint64_t correlationID);
+
+ void resetCrossTileIDs() override;
+
+ bool holdForFade() const override;
+ void markRenderedIdeal() override;
+ void markRenderedPreviously() override;
+ void performedFadePlacement() override;
protected:
const GeometryTileData* getData() {
@@ -93,7 +109,6 @@ protected:
private:
void markObsolete();
- void invokePlacement();
const std::string sourceID;
@@ -107,7 +122,6 @@ 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;
@@ -117,10 +131,19 @@ private:
optional<PremultipliedImage> iconAtlasImage;
std::unordered_map<std::string, std::shared_ptr<Bucket>> symbolBuckets;
- std::unique_ptr<CollisionTile> collisionTile;
+
+ const MapMode mode;
- util::Throttler placementThrottler;
+ 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 12bb84d7e3..cf74bf3647 100644
--- a/src/mbgl/tile/geometry_tile_worker.cpp
+++ b/src/mbgl/tile/geometry_tile_worker.cpp
@@ -1,7 +1,6 @@
#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/tile/geometry_tile.hpp>
-#include <mbgl/text/collision_tile.hpp>
#include <mbgl/layout/symbol_layout.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/renderer/group_by_layout.hpp>
@@ -24,20 +23,36 @@ using namespace style;
GeometryTileWorker::GeometryTileWorker(ActorRef<GeometryTileWorker> self_,
ActorRef<GeometryTile> parent_,
OverscaledTileID id_,
+ const std::string& sourceID_,
const std::atomic<bool>& obsolete_,
const MapMode mode_,
- const float pixelRatio_)
+ const float pixelRatio_,
+ const bool showCollisionBoxes_)
: self(std::move(self_)),
parent(std::move(parent_)),
id(std::move(id_)),
+ sourceID(sourceID_),
obsolete(obsolete_),
mode(mode_),
- pixelRatio(pixelRatio_) {
+ pixelRatio(pixelRatio_),
+ showCollisionBoxes(showCollisionBoxes_) {
}
GeometryTileWorker::~GeometryTileWorker() = default;
/*
+ NOTE: The comments below are technically correct, but currently
+ conceptually misleading. The change to foreground label placement
+ means that:
+ (1) "placement" here is a misnomer: the remaining role of
+ "attemptPlacement" is symbol buffer generation
+ (2) Once a tile has completed layout, we will only run
+ "attemptPlacement" once
+ (3) Tiles won't be rendered until "attemptPlacement" has run once
+
+ TODO: Simplify GeometryTileWorker to fit its new role
+ https://github.com/mapbox/mapbox-gl-native/issues/10457
+
GeometryTileWorker is a state machine. This is its transition diagram.
States are indicated by [state], lines are transitions triggered by
messages, (parentheses) are actions taken on transition.
@@ -88,7 +103,7 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_,
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -112,13 +127,13 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_,
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
-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) {
@@ -136,7 +151,7 @@ void GeometryTileWorker::setPlacementConfig(PlacementConfig placementConfig_, ui
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -144,14 +159,14 @@ void GeometryTileWorker::symbolDependenciesChanged() {
try {
switch (state) {
case Idle:
- if (hasPendingSymbolLayouts()) {
+ if (symbolLayoutsNeedPreparation) {
attemptPlacement();
coalesce();
}
break;
case Coalescing:
- if (hasPendingSymbolLayouts()) {
+ if (symbolLayoutsNeedPreparation) {
state = NeedPlacement;
}
break;
@@ -161,7 +176,7 @@ void GeometryTileWorker::symbolDependenciesChanged() {
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -187,7 +202,7 @@ void GeometryTileWorker::coalesced() {
break;
}
} catch (...) {
- parent.invoke(&GeometryTile::onError, std::current_exception());
+ parent.invoke(&GeometryTile::onError, std::current_exception(), correlationID);
}
}
@@ -216,7 +231,10 @@ void GeometryTileWorker::onGlyphsAvailable(GlyphMap newGlyphMap) {
symbolDependenciesChanged();
}
-void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap) {
+void GeometryTileWorker::onImagesAvailable(ImageMap newImageMap, uint64_t imageCorrelationID_) {
+ if (imageCorrelationID != imageCorrelationID_) {
+ return; // Ignore outdated image request replies.
+ }
imageMap = std::move(newImageMap);
pendingImageDependencies.clear();
symbolDependenciesChanged();
@@ -239,7 +257,7 @@ void GeometryTileWorker::requestNewGlyphs(const GlyphDependencies& glyphDependen
void GeometryTileWorker::requestNewImages(const ImageDependencies& imageDependencies) {
pendingImageDependencies = imageDependencies;
if (!pendingImageDependencies.empty()) {
- parent.invoke(&GeometryTile::getImages, pendingImageDependencies);
+ parent.invoke(&GeometryTile::getImages, std::make_pair(pendingImageDependencies, ++imageCorrelationID));
}
}
@@ -312,6 +330,7 @@ void GeometryTileWorker::redoLayout() {
auto layout = leader.as<RenderSymbolLayer>()->createLayout(
parameters, group, std::move(geometryLayer), glyphDependencies, imageDependencies);
symbolLayoutMap.emplace(leader.getID(), std::move(layout));
+ symbolLayoutsNeedPreparation = true;
} else {
const Filter& filter = leader.baseImpl->filter;
const std::string& sourceLayerID = leader.baseImpl->sourceLayer;
@@ -353,22 +372,11 @@ void GeometryTileWorker::redoLayout() {
std::move(buckets),
std::move(featureIndex),
*data ? (*data)->clone() : nullptr,
- correlationID
- });
+ }, correlationID);
attemptPlacement();
}
-bool GeometryTileWorker::hasPendingSymbolLayouts() const {
- for (const auto& symbolLayout : symbolLayouts) {
- if (symbolLayout->state == SymbolLayout::Pending) {
- return true;
- }
- }
-
- return false;
-}
-
bool GeometryTileWorker::hasPendingSymbolDependencies() const {
for (auto& glyphDependency : pendingGlyphDependencies) {
if (!glyphDependency.second.empty()) {
@@ -378,40 +386,46 @@ bool GeometryTileWorker::hasPendingSymbolDependencies() const {
return !pendingImageDependencies.empty();
}
-
void GeometryTileWorker::attemptPlacement() {
- if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) {
+ if (!data || !layers || hasPendingSymbolDependencies()) {
return;
}
- auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
- std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
-
optional<AlphaImage> glyphAtlasImage;
optional<PremultipliedImage> iconAtlasImage;
- for (auto& symbolLayout : symbolLayouts) {
- if (obsolete) {
- return;
- }
+ if (symbolLayoutsNeedPreparation) {
+ GlyphAtlas glyphAtlas = makeGlyphAtlas(glyphMap);
+ ImageAtlas imageAtlas = makeImageAtlas(imageMap);
+
+ glyphAtlasImage = std::move(glyphAtlas.image);
+ iconAtlasImage = std::move(imageAtlas.image);
- if (symbolLayout->state == SymbolLayout::Pending) {
- GlyphAtlas glyphAtlas = makeGlyphAtlas(glyphMap);
- ImageAtlas imageAtlas = makeImageAtlas(imageMap);
+ for (auto& symbolLayout : symbolLayouts) {
+ if (obsolete) {
+ return;
+ }
symbolLayout->prepare(glyphMap, glyphAtlas.positions,
- imageMap, imageAtlas.positions);
- symbolLayout->state = SymbolLayout::Placed;
+ imageMap, imageAtlas.positions,
+ id, sourceID);
+ }
+
+ symbolLayoutsNeedPreparation = false;
+ }
+
+ std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
- glyphAtlasImage = std::move(glyphAtlas.image);
- iconAtlasImage = std::move(imageAtlas.image);
+ for (auto& symbolLayout : symbolLayouts) {
+ if (obsolete) {
+ return;
}
if (!symbolLayout->hasSymbolInstances()) {
continue;
}
- std::shared_ptr<Bucket> bucket = symbolLayout->place(*collisionTile);
+ std::shared_ptr<Bucket> bucket = symbolLayout->place(showCollisionBoxes);
for (const auto& pair : symbolLayout->layerPaintProperties) {
buckets.emplace(pair.first, bucket);
}
@@ -419,11 +433,9 @@ void GeometryTileWorker::attemptPlacement() {
parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult {
std::move(buckets),
- std::move(collisionTile),
std::move(glyphAtlasImage),
std::move(iconAtlasImage),
- correlationID
- });
+ }, correlationID);
}
} // namespace mbgl
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 194477e7b8..cc86248cec 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -4,7 +4,6 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/style/image_impl.hpp>
#include <mbgl/text/glyph.hpp>
-#include <mbgl/text/placement_config.hpp>
#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/immutable.hpp>
@@ -28,17 +27,19 @@ 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);
+ void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID);
private:
void coalesced();
@@ -52,12 +53,12 @@ private:
void symbolDependenciesChanged();
bool hasPendingSymbolDependencies() const;
- bool hasPendingSymbolLayouts() 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;
@@ -71,17 +72,20 @@ private:
State state = Idle;
uint64_t correlationID = 0;
+ uint64_t imageCorrelationID = 0;
// 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;
GlyphDependencies pendingGlyphDependencies;
ImageDependencies pendingImageDependencies;
GlyphMap glyphMap;
ImageMap imageMap;
+
+ bool showCollisionBoxes;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile.cpp b/src/mbgl/tile/raster_tile.cpp
index 8a92c40e4a..85fcea77b7 100644
--- a/src/mbgl/tile/raster_tile.cpp
+++ b/src/mbgl/tile/raster_tile.cpp
@@ -8,7 +8,7 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/buckets/raster_bucket.hpp>
-#include <mbgl/util/run_loop.hpp>
+#include <mbgl/actor/scheduler.hpp>
namespace mbgl {
@@ -17,7 +17,7 @@ RasterTile::RasterTile(const OverscaledTileID& id_,
const Tileset& tileset)
: Tile(id_),
loader(*this, id_, parameters, tileset),
- mailbox(std::make_shared<Mailbox>(*util::RunLoop::Get())),
+ mailbox(std::make_shared<Mailbox>(*Scheduler::GetCurrent())),
worker(parameters.workerScheduler,
ActorRef<RasterTile>(*this, mailbox)) {
}
@@ -29,29 +29,35 @@ void RasterTile::cancel() {
void RasterTile::setError(std::exception_ptr err) {
loaded = true;
- renderable = false;
observer->onTileError(*this, err);
}
-void RasterTile::setData(std::shared_ptr<const std::string> data,
- optional<Timestamp> modified_,
- optional<Timestamp> expires_) {
+void RasterTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) {
modified = modified_;
expires = expires_;
- worker.invoke(&RasterTileWorker::parse, data);
}
-void RasterTile::onParsed(std::unique_ptr<Bucket> result) {
+void RasterTile::setData(std::shared_ptr<const std::string> data) {
+ pending = true;
+ ++correlationID;
+ worker.invoke(&RasterTileWorker::parse, data, correlationID);
+}
+
+void RasterTile::onParsed(std::unique_ptr<RasterBucket> result, const uint64_t resultCorrelationID) {
bucket = std::move(result);
loaded = true;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
renderable = bucket ? true : false;
observer->onTileChanged(*this);
}
-void RasterTile::onError(std::exception_ptr err) {
- bucket.reset();
+void RasterTile::onError(std::exception_ptr err, const uint64_t resultCorrelationID) {
loaded = true;
- renderable = false;
+ if (resultCorrelationID == correlationID) {
+ pending = false;
+ }
observer->onTileError(*this, err);
}
@@ -65,7 +71,13 @@ Bucket* RasterTile::getBucket(const style::Layer::Impl&) const {
return bucket.get();
}
-void RasterTile::setNecessity(Necessity necessity) {
+void RasterTile::setMask(TileMask&& mask) {
+ if (bucket) {
+ bucket->setMask(std::move(mask));
+ }
+}
+
+void RasterTile::setNecessity(TileNecessity necessity) {
loader.setNecessity(necessity);
}
diff --git a/src/mbgl/tile/raster_tile.hpp b/src/mbgl/tile/raster_tile.hpp
index 51075a2dbc..192769ed8f 100644
--- a/src/mbgl/tile/raster_tile.hpp
+++ b/src/mbgl/tile/raster_tile.hpp
@@ -9,6 +9,7 @@ namespace mbgl {
class Tileset;
class TileParameters;
+class RasterBucket;
namespace style {
class Layer;
@@ -21,20 +22,21 @@ public:
const Tileset&);
~RasterTile() final;
- void setNecessity(Necessity) final;
+ void setNecessity(TileNecessity) final;
void setError(std::exception_ptr);
- void setData(std::shared_ptr<const std::string> data,
- optional<Timestamp> modified_,
- optional<Timestamp> expires_);
+ 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;
- void onParsed(std::unique_ptr<Bucket> result);
- void onError(std::exception_ptr);
+ void setMask(TileMask&&) override;
+
+ void onParsed(std::unique_ptr<RasterBucket> result, uint64_t correlationID);
+ void onError(std::exception_ptr, uint64_t correlationID);
private:
TileLoader<RasterTile> loader;
@@ -42,9 +44,11 @@ private:
std::shared_ptr<Mailbox> mailbox;
Actor<RasterTileWorker> worker;
+ uint64_t correlationID = 0;
+
// Contains the Bucket object for the tile. Buckets are render
// objects and they get added by tile parsing operations.
- std::unique_ptr<Bucket> bucket;
+ std::unique_ptr<RasterBucket> bucket;
};
} // namespace mbgl
diff --git a/src/mbgl/tile/raster_tile_worker.cpp b/src/mbgl/tile/raster_tile_worker.cpp
index 86fb5f181d..4afa876429 100644
--- a/src/mbgl/tile/raster_tile_worker.cpp
+++ b/src/mbgl/tile/raster_tile_worker.cpp
@@ -10,17 +10,17 @@ RasterTileWorker::RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTi
: parent(std::move(parent_)) {
}
-void RasterTileWorker::parse(std::shared_ptr<const std::string> data) {
+void RasterTileWorker::parse(std::shared_ptr<const std::string> data, uint64_t correlationID) {
if (!data) {
- parent.invoke(&RasterTile::onParsed, nullptr); // No data; empty tile.
+ parent.invoke(&RasterTile::onParsed, nullptr, correlationID); // No data; empty tile.
return;
}
try {
- auto bucket = std::make_unique<RasterBucket>(util::unpremultiply(decodeImage(*data)));
- parent.invoke(&RasterTile::onParsed, std::move(bucket));
+ auto bucket = std::make_unique<RasterBucket>(decodeImage(*data));
+ parent.invoke(&RasterTile::onParsed, std::move(bucket), correlationID);
} catch (...) {
- parent.invoke(&RasterTile::onError, std::current_exception());
+ parent.invoke(&RasterTile::onError, std::current_exception(), correlationID);
}
}
diff --git a/src/mbgl/tile/raster_tile_worker.hpp b/src/mbgl/tile/raster_tile_worker.hpp
index 44bc37ca5d..520973c3c3 100644
--- a/src/mbgl/tile/raster_tile_worker.hpp
+++ b/src/mbgl/tile/raster_tile_worker.hpp
@@ -13,7 +13,7 @@ class RasterTileWorker {
public:
RasterTileWorker(ActorRef<RasterTileWorker>, ActorRef<RasterTile>);
- void parse(std::shared_ptr<const std::string> data);
+ void parse(std::shared_ptr<const std::string> data, uint64_t correlationID);
private:
ActorRef<RasterTile> parent;
diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp
index 5080d42933..85899a98cb 100644
--- a/src/mbgl/tile/tile.cpp
+++ b/src/mbgl/tile/tile.cpp
@@ -1,9 +1,9 @@
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/tile_observer.hpp>
#include <mbgl/renderer/buckets/debug_bucket.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/logging.hpp>
-#include <mbgl/map/query.hpp>
namespace mbgl {
@@ -18,7 +18,7 @@ void Tile::setObserver(TileObserver* observer_) {
observer = observer_;
}
-void Tile::setTriedOptional() {
+void Tile::setTriedCache() {
triedOptional = true;
observer->onTileChanged(*this);
}
@@ -33,8 +33,9 @@ void Tile::queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>&,
const GeometryCoordinates&,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions&) {}
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions&,
+ const CollisionIndex&) {}
void Tile::querySourceFeatures(
std::vector<Feature>&,
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index a925d88af3..1186b74111 100644
--- a/src/mbgl/tile/tile.hpp
+++ b/src/mbgl/tile/tile.hpp
@@ -6,6 +6,8 @@
#include <mbgl/util/feature.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/tile/tile_id.hpp>
+#include <mbgl/tile/tile_necessity.hpp>
+#include <mbgl/renderer/tile_mask.hpp>
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/storage/resource.hpp>
@@ -21,11 +23,12 @@ namespace mbgl {
class DebugBucket;
class TransformState;
class TileObserver;
-class PlacementConfig;
-class RenderStyle;
+class RenderLayer;
class RenderedQueryOptions;
class SourceQueryOptions;
+class CollisionIndex;
+
namespace gl {
class Context;
} // namespace gl
@@ -37,14 +40,7 @@ public:
void setObserver(TileObserver* observer);
- // Tiles can have two states: optional or required.
- // - optional means that only low-cost actions should be taken to obtain the data
- // (e.g. load from cache, but accept stale data)
- // - required means that every effort should be taken to obtain the data (e.g. load
- // from internet and keep the data fresh if it expires)
- using Necessity = Resource::Necessity;
-
- virtual void setNecessity(Necessity) = 0;
+ virtual void setNecessity(TileNecessity) {}
// Mark this tile as no longer needed and cancel any pending work.
virtual void cancel() = 0;
@@ -52,25 +48,27 @@ public:
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&&) {}
virtual void queryRenderedFeatures(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
const TransformState&,
- const RenderStyle&,
- const RenderedQueryOptions& options);
+ const std::vector<const RenderLayer*>&,
+ const RenderedQueryOptions& options,
+ const CollisionIndex&);
virtual void querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions&);
- void setTriedOptional();
+ void setTriedCache();
// Returns true when the tile source has received a first response, regardless of whether a load
// error occurred or actual data was loaded.
- bool hasTriedOptional() const {
+ bool hasTriedCache() const {
return triedOptional;
}
@@ -96,6 +94,22 @@ 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() {}
+
+ virtual void resetCrossTileIDs() {};
void dumpDebugLogs() const;
diff --git a/src/mbgl/tile/tile_id_hash.cpp b/src/mbgl/tile/tile_id_hash.cpp
new file mode 100644
index 0000000000..4a1f185817
--- /dev/null
+++ b/src/mbgl/tile/tile_id_hash.cpp
@@ -0,0 +1,29 @@
+#include <mbgl/tile/tile_id.hpp>
+
+#include <boost/functional/hash.hpp>
+
+namespace std {
+
+size_t hash<mbgl::CanonicalTileID>::operator()(const mbgl::CanonicalTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, id.x);
+ boost::hash_combine(seed, id.y);
+ boost::hash_combine(seed, id.z);
+ return seed;
+}
+
+size_t hash<mbgl::UnwrappedTileID>::operator()(const mbgl::UnwrappedTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
+ boost::hash_combine(seed, id.wrap);
+ return seed;
+}
+
+size_t hash<mbgl::OverscaledTileID>::operator()(const mbgl::OverscaledTileID& id) const {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, std::hash<mbgl::CanonicalTileID>{}(id.canonical));
+ boost::hash_combine(seed, id.overscaledZ);
+ return seed;
+}
+
+} // namespace std
diff --git a/src/mbgl/tile/tile_id_io.cpp b/src/mbgl/tile/tile_id_io.cpp
index f6adbf183f..d8be6b93d6 100644
--- a/src/mbgl/tile/tile_id_io.cpp
+++ b/src/mbgl/tile/tile_id_io.cpp
@@ -6,6 +6,8 @@
namespace mbgl {
::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs) {
+ // Uncomment this to create code instead of shorthands.
+ // return os << "CanonicalTileID{ " << uint32_t(rhs.z) << ", " << rhs.x << ", " << rhs.y << " }";
return os << uint32_t(rhs.z) << "/" << rhs.x << "/" << rhs.y;
}
@@ -26,6 +28,9 @@ std::string toString(const OverscaledTileID& rhs) {
} // namespace util
::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs) {
+ // Uncomment this to create code instead of shorthands.
+ // return os << "UnwrappedTileID{ " << uint32_t(rhs.wrap) << ", { " << uint32_t(rhs.canonical.z)
+ // << ", " << rhs.canonical.x << ", " << rhs.canonical.y << " } }";
return os << rhs.canonical << (rhs.wrap >= 0 ? "+" : "") << rhs.wrap;
}
diff --git a/src/mbgl/tile/tile_loader.hpp b/src/mbgl/tile/tile_loader.hpp
index bc408ebaf6..92ca74330f 100644
--- a/src/mbgl/tile/tile_loader.hpp
+++ b/src/mbgl/tile/tile_loader.hpp
@@ -21,12 +21,10 @@ public:
const Tileset&);
~TileLoader();
- using Necessity = Resource::Necessity;
-
- void setNecessity(Necessity newNecessity) {
+ void setNecessity(TileNecessity newNecessity) {
if (newNecessity != necessity) {
necessity = newNecessity;
- if (necessity == Necessity::Required) {
+ if (necessity == TileNecessity::Required) {
makeRequired();
} else {
makeOptional();
@@ -45,12 +43,12 @@ private:
// an up-to-date version or load new data
void makeOptional();
- void loadOptional();
+ void loadFromCache();
void loadedData(const Response&);
- void loadRequired();
+ void loadFromNetwork();
T& tile;
- Necessity necessity;
+ TileNecessity necessity;
Resource resource;
FileSource& fileSource;
std::unique_ptr<AsyncRequest> request;
diff --git a/src/mbgl/tile/tile_loader_impl.hpp b/src/mbgl/tile/tile_loader_impl.hpp
index 899cbaf9b0..1b29638269 100644
--- a/src/mbgl/tile/tile_loader_impl.hpp
+++ b/src/mbgl/tile/tile_loader_impl.hpp
@@ -15,32 +15,31 @@ TileLoader<T>::TileLoader(T& tile_,
const TileParameters& parameters,
const Tileset& tileset)
: tile(tile_),
- necessity(Necessity::Optional),
+ necessity(TileNecessity::Optional),
resource(Resource::tile(
tileset.tiles.at(0),
parameters.pixelRatio,
id.canonical.x,
id.canonical.y,
id.canonical.z,
- tileset.scheme)),
+ tileset.scheme,
+ Resource::LoadingMethod::CacheOnly)),
fileSource(parameters.fileSource) {
assert(!request);
- if (fileSource.supportsOptionalRequests()) {
+ if (fileSource.supportsCacheOnlyRequests()) {
// When supported, the first request is always optional, even if the TileLoader
// is marked as required. That way, we can let the first optional request continue
// to load when the TileLoader is later changed from required to optional. If we
// started out with a required request, we'd have to cancel everything, including the
// initial optional part of the request.
- loadOptional();
+ loadFromCache();
+ } else if (necessity == TileNecessity::Required) {
+ // When the file source doesn't support cache-only requests, and we definiitely need this
+ // data, we can start out with a network request immediately.
+ loadFromNetwork();
} else {
- // When the FileSource doesn't support optional requests, we do nothing until the
+ // When the FileSource doesn't support cache-only requests, we do nothing until the
// data is definitely required.
- if (necessity == Necessity::Required) {
- loadRequired();
- } else {
- // We're using this field to check whether the pending request is optional or required.
- resource.necessity = Resource::Optional;
- }
}
}
@@ -48,26 +47,31 @@ template <typename T>
TileLoader<T>::~TileLoader() = default;
template <typename T>
-void TileLoader<T>::loadOptional() {
+void TileLoader<T>::loadFromCache() {
assert(!request);
- resource.necessity = Resource::Optional;
+ resource.loadingMethod = Resource::LoadingMethod::CacheOnly;
request = fileSource.request(resource, [this](Response res) {
request.reset();
- tile.setTriedOptional();
+ tile.setTriedCache();
if (res.error && res.error->reason == Response::Error::Reason::NotFound) {
- // When the optional request could not be satisfied, don't treat it as an error.
- // Instead, we make sure that the next request knows that there has been an optional
- // request before by setting one of the prior* fields.
- resource.priorExpires = Timestamp{ Seconds::zero() };
+ // When the cache-only request could not be satisfied, don't treat it as an error.
+ // A cache lookup could still return data, _and_ an error, in particular when we were
+ // able to find the data, but it is expired and the Cache-Control headers indicated that
+ // we aren't allowed to use expired responses. In this case, we still get the data which
+ // we can use in our conditional network request.
+ resource.priorModified = res.modified;
+ resource.priorExpires = res.expires;
+ resource.priorEtag = res.etag;
+ resource.priorData = res.data;
} else {
loadedData(res);
}
- if (necessity == Necessity::Required) {
- loadRequired();
+ if (necessity == TileNecessity::Required) {
+ loadFromNetwork();
}
});
}
@@ -75,14 +79,15 @@ void TileLoader<T>::loadOptional() {
template <typename T>
void TileLoader<T>::makeRequired() {
if (!request) {
- loadRequired();
+ loadFromNetwork();
}
}
template <typename T>
void TileLoader<T>::makeOptional() {
- if (resource.necessity == Resource::Required && request) {
- // Abort a potential HTTP request.
+ if (resource.loadingMethod == Resource::LoadingMethod::NetworkOnly && request) {
+ // Abort the current request, but only when we know that we're specifically querying for a
+ // network resource only.
request.reset();
}
}
@@ -95,19 +100,23 @@ void TileLoader<T>::loadedData(const Response& res) {
resource.priorExpires = res.expires;
// Do not notify the tile; when we get this message, it already has the current
// version of the data.
+ tile.setMetadata(res.modified, res.expires);
} else {
resource.priorModified = res.modified;
resource.priorExpires = res.expires;
resource.priorEtag = res.etag;
- tile.setData(res.noContent ? nullptr : res.data, res.modified, res.expires);
+ tile.setMetadata(res.modified, res.expires);
+ tile.setData(res.noContent ? nullptr : res.data);
}
}
template <typename T>
-void TileLoader<T>::loadRequired() {
+void TileLoader<T>::loadFromNetwork() {
assert(!request);
- resource.necessity = Resource::Required;
+ // Instead of using Resource::LoadingMethod::All, we're first doing a CacheOnly, and then a
+ // NetworkOnly request.
+ resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
request = fileSource.request(resource, [this](Response res) { loadedData(res); });
}
diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp
index e2e700b7b7..0756d3e526 100644
--- a/src/mbgl/tile/vector_tile.cpp
+++ b/src/mbgl/tile/vector_tile.cpp
@@ -12,16 +12,16 @@ VectorTile::VectorTile(const OverscaledTileID& id_,
: GeometryTile(id_, sourceID_, parameters), loader(*this, id_, parameters, tileset) {
}
-void VectorTile::setNecessity(Necessity necessity) {
+void VectorTile::setNecessity(TileNecessity necessity) {
loader.setNecessity(necessity);
}
-void VectorTile::setData(std::shared_ptr<const std::string> data_,
- optional<Timestamp> modified_,
- optional<Timestamp> expires_) {
+void VectorTile::setMetadata(optional<Timestamp> modified_, optional<Timestamp> expires_) {
modified = modified_;
expires = expires_;
+}
+void VectorTile::setData(std::shared_ptr<const std::string> data_) {
GeometryTile::setData(data_ ? std::make_unique<VectorTileData>(data_) : nullptr);
}
diff --git a/src/mbgl/tile/vector_tile.hpp b/src/mbgl/tile/vector_tile.hpp
index 566cde4f37..7dae414fef 100644
--- a/src/mbgl/tile/vector_tile.hpp
+++ b/src/mbgl/tile/vector_tile.hpp
@@ -15,10 +15,9 @@ public:
const TileParameters&,
const Tileset&);
- void setNecessity(Necessity) final;
- void setData(std::shared_ptr<const std::string> data,
- optional<Timestamp> modified,
- optional<Timestamp> expires);
+ void setNecessity(TileNecessity) final;
+ void setMetadata(optional<Timestamp> modified, optional<Timestamp> expires);
+ void setData(std::shared_ptr<const std::string> data);
private:
TileLoader<VectorTile> loader;
diff --git a/src/mbgl/util/chrono.cpp b/src/mbgl/util/chrono.cpp
index 5c8fd3c0ff..a880093b74 100644
--- a/src/mbgl/util/chrono.cpp
+++ b/src/mbgl/util/chrono.cpp
@@ -3,6 +3,13 @@
#include <parsedate/parsedate.h>
#include <cstdio>
+#include <ctime>
+
+#if defined(_WINDOWS)
+#define _gmtime(t, i) gmtime_s(i, t)
+#else
+#define _gmtime(t, i) gmtime_r(t, i)
+#endif
namespace mbgl {
namespace util {
@@ -14,7 +21,7 @@ static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
std::string rfc1123(Timestamp timestamp) {
std::time_t time = std::chrono::system_clock::to_time_t(timestamp);
std::tm info;
- gmtime_r(&time, &info);
+ _gmtime(&time, &info);
char buffer[30];
snprintf(buffer, 30, "%s, %02d %s %4d %02d:%02d:%02d GMT", week[info.tm_wday], info.tm_mday,
months[info.tm_mon], 1900 + info.tm_year, info.tm_hour, info.tm_min, info.tm_sec);
@@ -24,7 +31,7 @@ std::string rfc1123(Timestamp timestamp) {
std::string iso8601(Timestamp timestamp) {
std::time_t time = std::chrono::system_clock::to_time_t(timestamp);
std::tm info;
- gmtime_r(&time, &info);
+ _gmtime(&time, &info);
char buffer[30];
std::strftime(buffer, sizeof(buffer), "%F %T", &info);
return buffer;
diff --git a/src/mbgl/util/constants.cpp b/src/mbgl/util/constants.cpp
index 9faef140ef..56f78c9885 100644
--- a/src/mbgl/util/constants.cpp
+++ b/src/mbgl/util/constants.cpp
@@ -11,7 +11,6 @@ const bool tileParseWarnings = false;
const bool styleParseWarnings = false;
const bool spriteWarnings = false;
const bool renderWarnings = false;
-const bool renderTree = false;
const bool labelTextMissingWarning = true;
const bool missingFontStackWarning = true;
const bool missingFontFaceWarning = true;
@@ -22,7 +21,6 @@ const bool tileParseWarnings = false;
const bool styleParseWarnings = false;
const bool spriteWarnings = false;
const bool renderWarnings = false;
-const bool renderTree = false;
const bool labelTextMissingWarning = false;
const bool missingFontStackWarning = false;
const bool missingFontFaceWarning = false;
diff --git a/src/mbgl/util/dtoa.cpp b/src/mbgl/util/dtoa.cpp
index dd4fba0f89..0e3aef6117 100644
--- a/src/mbgl/util/dtoa.cpp
+++ b/src/mbgl/util/dtoa.cpp
@@ -1,10 +1,18 @@
#include "dtoa.hpp"
+// Clang/C2 on Windows 64-bits can't parse rapidjson's dtoa
+// and it was causing the compiler to crash.
+#if !defined(_WINDOWS)
#include <rapidjson/internal/dtoa.h>
+#endif
+
+#include <mbgl/util/string.hpp>
namespace mbgl {
namespace util {
+#if !defined(_WINDOWS)
+
namespace {
// From https://github.com/miloyip/rapidjson/blob/master/include/rapidjson/internal/dtoa.h
@@ -101,5 +109,13 @@ std::string dtoa(double value) {
return data;
}
+#else
+
+std::string dtoa(double value) {
+ return std::to_string(value);
+}
+
+#endif
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/dtoa.hpp b/src/mbgl/util/dtoa.hpp
index db7d309452..4cb81a94be 100644
--- a/src/mbgl/util/dtoa.hpp
+++ b/src/mbgl/util/dtoa.hpp
@@ -5,7 +5,6 @@
namespace mbgl {
namespace util {
-char* dtoa(double value, char* buffer);
std::string dtoa(double value);
} // end namespace util
diff --git a/src/mbgl/util/geojson.cpp b/src/mbgl/util/geojson_impl.cpp
index d1608e09fe..d1608e09fe 100644
--- a/src/mbgl/util/geojson.cpp
+++ b/src/mbgl/util/geojson_impl.cpp
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 40711232ff..ce31a06c5e 100644
--- a/src/mbgl/util/http_header.cpp
+++ b/src/mbgl/util/http_header.cpp
@@ -1,5 +1,7 @@
#include <mbgl/util/http_header.hpp>
+#include <mbgl/util/string.hpp>
+
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-parameter"
diff --git a/src/mbgl/util/http_timeout.cpp b/src/mbgl/util/http_timeout.cpp
index ca9a93498f..3456369250 100644
--- a/src/mbgl/util/http_timeout.cpp
+++ b/src/mbgl/util/http_timeout.cpp
@@ -1,6 +1,8 @@
#include <mbgl/util/http_timeout.hpp>
#include <mbgl/util/constants.hpp>
+#include <cassert>
+
namespace mbgl {
namespace http {
diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp
index ada6f6526c..1fc13bfb7d 100644
--- a/src/mbgl/util/i18n.cpp
+++ b/src/mbgl/util/i18n.cpp
@@ -15,7 +15,7 @@ namespace {
return codepoint >= first && codepoint <= last; \
}
-// The following table comes from <http://www.unicode.org/Public/9.0.0/ucd/Blocks.txt>.
+// The following table comes from <http://www.unicode.org/Public/10.0.0/ucd/Blocks.txt>.
// Keep it synchronized with <http://www.unicode.org/Public/UCD/latest/ucd/Blocks.txt>.
// DEFINE_IS_IN_UNICODE_BLOCK(BasicLatin, 0x0000, 0x007F)
@@ -37,6 +37,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(ArabicSupplement, 0x0750, 0x077F)
// DEFINE_IS_IN_UNICODE_BLOCK(NKo, 0x07C0, 0x07FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Samaritan, 0x0800, 0x083F)
// DEFINE_IS_IN_UNICODE_BLOCK(Mandaic, 0x0840, 0x085F)
+// DEFINE_IS_IN_UNICODE_BLOCK(Syriac Supplement, 0x0860, 0x086F)
DEFINE_IS_IN_UNICODE_BLOCK(ArabicExtendedA, 0x08A0, 0x08FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Devanagari, 0x0900, 0x097F)
// DEFINE_IS_IN_UNICODE_BLOCK(Bengali, 0x0980, 0x09FF)
@@ -239,9 +240,12 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF)
// DEFINE_IS_IN_UNICODE_BLOCK(Takri, 0x11680, 0x116CF)
// DEFINE_IS_IN_UNICODE_BLOCK(Ahom, 0x11700, 0x1173F)
// DEFINE_IS_IN_UNICODE_BLOCK(WarangCiti, 0x118A0, 0x118FF)
+// DEFINE_IS_IN_UNICODE_BLOCK(ZanabazarSquare, 0x11A00, 0x11A4F)
+// DEFINE_IS_IN_UNICODE_BLOCK(Soyombo, 0x11A50, 0x11AAF)
// DEFINE_IS_IN_UNICODE_BLOCK(PauCinHau, 0x11AC0, 0x11AFF)
// DEFINE_IS_IN_UNICODE_BLOCK(Bhaiksuki, 0x11C00, 0x11C6F)
// DEFINE_IS_IN_UNICODE_BLOCK(Marchen, 0x11C70, 0x11CBF)
+// DEFINE_IS_IN_UNICODE_BLOCK(MasaramGondi, 0x11D00, 0x11D5F)
// DEFINE_IS_IN_UNICODE_BLOCK(Cuneiform, 0x12000, 0x123FF)
// DEFINE_IS_IN_UNICODE_BLOCK(CuneiformNumbersandPunctuation, 0x12400, 0x1247F)
// DEFINE_IS_IN_UNICODE_BLOCK(EarlyDynasticCuneiform, 0x12480, 0x1254F)
@@ -256,6 +260,8 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF)
// DEFINE_IS_IN_UNICODE_BLOCK(Tangut, 0x17000, 0x187FF)
// DEFINE_IS_IN_UNICODE_BLOCK(TangutComponents, 0x18800, 0x18AFF)
// DEFINE_IS_IN_UNICODE_BLOCK(KanaSupplement, 0x1B000, 0x1B0FF)
+// DEFINE_IS_IN_UNICODE_BLOCK(KanaExtendedA, 0x1B100, 0x1B12F)
+// DEFINE_IS_IN_UNICODE_BLOCK(Nushu, 0x1B170, 0x1B2FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Duployan, 0x1BC00, 0x1BC9F)
// DEFINE_IS_IN_UNICODE_BLOCK(ShorthandFormatControls, 0x1BCA0, 0x1BCAF)
// DEFINE_IS_IN_UNICODE_BLOCK(ByzantineMusicalSymbols, 0x1D000, 0x1D0FF)
@@ -286,6 +292,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(HalfwidthandFullwidthForms, 0xFF00, 0xFFEF)
// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionC, 0x2A700, 0x2B73F)
// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionD, 0x2B740, 0x2B81F)
// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionE, 0x2B820, 0x2CEAF)
+// DEFINE_IS_IN_UNICODE_BLOCK(CJKUnifiedIdeographsExtensionF, 0x2CEB0, 0x2EBEF)
// DEFINE_IS_IN_UNICODE_BLOCK(CJKCompatibilityIdeographsSupplement, 0x2F800, 0x2FA1F)
// DEFINE_IS_IN_UNICODE_BLOCK(Tags, 0xE0000, 0xE007F)
// DEFINE_IS_IN_UNICODE_BLOCK(VariationSelectorsSupplement, 0xE0100, 0xE01EF)
@@ -375,14 +382,21 @@ bool allowsIdeographicBreaking(char16_t chr) {
// return (isInTangut(chr)
// || isInTangutComponents(chr)
// || isInIdeographicSymbolsandPunctuation(chr)
+ // || isInNushu(chr)
// || isInEnclosedIdeographicSupplement(chr)
// || isInCJKUnifiedIdeographsExtensionB(chr)
// || isInCJKUnifiedIdeographsExtensionC(chr)
// || isInCJKUnifiedIdeographsExtensionD(chr)
// || isInCJKUnifiedIdeographsExtensionE(chr)
+ // || isInCJKUnifiedIdeographsExtensionF(chr)
// || isInCJKCompatibilityIdeographsSupplement(chr));
}
+bool allowsFixedWidthGlyphGeneration(char16_t chr) {
+ // Mirrors conservative set of characters used in glyph_manager.js/_tinySDF
+ return isInCJKUnifiedIdeographs(chr) || isInHangulSyllables(chr);
+}
+
bool allowsVerticalWritingMode(const std::u16string& string) {
for (char32_t chr : string) {
if (hasUprightVerticalOrientation(chr)) {
@@ -393,7 +407,7 @@ bool allowsVerticalWritingMode(const std::u16string& string) {
}
// The following logic comes from
-// <http://www.unicode.org/Public/vertical/revision-16/VerticalOrientation-16.txt>.
+// <http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt>.
// The data file denotes with “U” or “Tu” any codepoint that may be drawn
// upright in vertical text but does not distinguish between upright and
// “neutral” characters.
@@ -457,6 +471,8 @@ bool hasUprightVerticalOrientation(char16_t chr) {
// if (isInTangut(chr)) return true;
// if (isInTangutComponents(chr)) return true;
// if (isInKanaSupplement(chr)) return true;
+ // if (isInKanaExtendedA(chr)) return true;
+ // if (isInNushu(chr)) return true;
// if (isInByzantineMusicalSymbols(chr)) return true;
// if (isInMusicalSymbols(chr)) return true;
// if (isInTaiXuanJingSymbols(chr)) return true;
@@ -478,6 +494,7 @@ bool hasUprightVerticalOrientation(char16_t chr) {
// if (isInCJKUnifiedIdeographsExtensionC(chr)) return true;
// if (isInCJKUnifiedIdeographsExtensionD(chr)) return true;
// if (isInCJKUnifiedIdeographsExtensionE(chr)) return true;
+ // if (isInCJKUnifiedIdeographsExtensionF(chr)) return true;
// if (isInCJKCompatibilityIdeographsSupplement(chr)) return true;
return false;
@@ -542,7 +559,7 @@ std::u16string verticalizePunctuation(const std::u16string& input) {
std::u16string output;
for (size_t i = 0; i < input.size(); i++) {
- char16_t nextCharCode = i < input.size() ? input[i + 1] : 0;
+ char16_t nextCharCode = i < input.size() - 1 ? input[i + 1] : 0;
char16_t prevCharCode = i ? input[i - 1] : 0;
bool canReplacePunctuation =
diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp
index 61c5a1ea96..b3960c743c 100644
--- a/src/mbgl/util/i18n.hpp
+++ b/src/mbgl/util/i18n.hpp
@@ -23,6 +23,10 @@ bool allowsIdeographicBreaking(const std::u16string& string);
by the given Unicode codepoint due to ideographic breaking. */
bool allowsIdeographicBreaking(char16_t chr);
+/** Conservative set of characters expected to have relatively fixed sizes and
+ advances */
+bool allowsFixedWidthGlyphGeneration(char16_t chr);
+
/** Returns whether any substring of the given string can be drawn as vertical
text with upright glyphs. */
bool allowsVerticalWritingMode(const std::u16string& string);
diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp
index 9adc3b8988..6a6ed7b250 100644
--- a/src/mbgl/util/io.cpp
+++ b/src/mbgl/util/io.cpp
@@ -6,8 +6,6 @@
#include <sstream>
#include <fstream>
-#include <unistd.h>
-
namespace mbgl {
namespace util {
@@ -43,8 +41,8 @@ optional<std::string> readFile(const std::string &filename) {
}
void deleteFile(const std::string& filename) {
- const int ret = unlink(filename.c_str());
- if (ret == -1) {
+ const int ret = std::remove(filename.c_str());
+ if (ret != 0) {
throw IOException(errno, "failed to unlink file");
}
}
diff --git a/src/mbgl/util/mapbox.cpp b/src/mbgl/util/mapbox.cpp
index 8cbc85d492..802b527a26 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) {
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/mat4.cpp b/src/mbgl/util/mat4.cpp
index d3d3617b7b..0ad0d371e5 100644
--- a/src/mbgl/util/mat4.cpp
+++ b/src/mbgl/util/mat4.cpp
@@ -336,10 +336,11 @@ void multiply(mat4& out, const mat4& a, const mat4& b) {
}
void transformMat4(vec4& out, const vec4& a, const mat4& m) {
- out[0] = m[0] * a[0] + m[4] * a[1] + m[8] * a[2] + m[12] * a[3];
- out[1] = m[1] * a[0] + m[5] * a[1] + m[9] * a[2] + m[13] * a[3];
- out[2] = m[2] * a[0] + m[6] * a[1] + m[10] * a[2] + m[14] * a[3];
- out[3] = m[3] * a[0] + m[7] * a[1] + m[11] * a[2] + m[15] * a[3];
+ double x = a[0], y = a[1], z = a[2], w = a[3];
+ out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
+ out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
+ out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
+ out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
}
} // namespace matrix
diff --git a/src/mbgl/util/math.hpp b/src/mbgl/util/math.hpp
index f969ecaedd..c18ce0c254 100644
--- a/src/mbgl/util/math.hpp
+++ b/src/mbgl/util/math.hpp
@@ -77,9 +77,9 @@ T mag(const S& a) {
return std::sqrt(a.x * a.x + a.y * a.y);
}
-template <typename S>
+template <typename T = double, typename S>
S unit(const S& a) {
- auto magnitude = mag(a);
+ auto magnitude = mag<T>(a);
if (magnitude == 0) {
return a;
}
@@ -106,5 +106,18 @@ T smoothstep(T edge0, T edge1, T x) {
return t * t * (T(3) - T(2) * t);
}
+template <typename T>
+inline T division(const T dividend, const T divisor, const T nan) {
+ if (divisor == 0) {
+ if (dividend == 0) {
+ return nan;
+ } else {
+ return ::copysign(std::numeric_limits<T>::infinity(), dividend);
+ }
+ } else {
+ return dividend / divisor;
+ }
+}
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/offscreen_texture.cpp b/src/mbgl/util/offscreen_texture.cpp
index 77a1416e48..339e74b250 100644
--- a/src/mbgl/util/offscreen_texture.cpp
+++ b/src/mbgl/util/offscreen_texture.cpp
@@ -11,20 +11,22 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default;
class OffscreenTexture::Impl {
public:
- Impl(gl::Context& context_, const Size size_, OffscreenTextureAttachment type_)
- : context(context_), size(std::move(size_)), type(type_) {
+ Impl(gl::Context& context_, const Size size_)
+ : context(context_), size(std::move(size_)) {
+ assert(!size.isEmpty());
+ }
+ Impl(gl::Context& context_,
+ const Size size_,
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_)
+ : context(context_), size(std::move(size_)), depth(&depth_) {
assert(!size.isEmpty());
}
void bind() {
if (!framebuffer) {
texture = context.createTexture(size, gl::TextureFormat::RGBA);
-
- if (type == OffscreenTextureAttachment::Depth) {
- gl::Renderbuffer<gl::RenderbufferType::DepthComponent> depth =
- context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size);
- framebuffer = context.createFramebuffer(*texture, depth);
-
+ if (depth) {
+ framebuffer = context.createFramebuffer(*texture, *depth);
} else {
framebuffer = context.createFramebuffer(*texture);
}
@@ -32,7 +34,7 @@ public:
context.bindFramebuffer = framebuffer->framebuffer;
}
- context.activeTexture = 0;
+ context.activeTextureUnit = 0;
context.scissorTest = false;
context.viewport = { 0, 0, size };
}
@@ -53,15 +55,21 @@ public:
private:
gl::Context& context;
const Size size;
- OffscreenTextureAttachment type;
optional<gl::Framebuffer> framebuffer;
optional<gl::Texture> texture;
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>* depth = nullptr;
};
OffscreenTexture::OffscreenTexture(gl::Context& context,
+ const Size size)
+ : impl(std::make_unique<Impl>(context, std::move(size))) {
+ assert(!size.isEmpty());
+}
+
+OffscreenTexture::OffscreenTexture(gl::Context& context,
const Size size,
- OffscreenTextureAttachment type)
- : impl(std::make_unique<Impl>(context, std::move(size), type)) {
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer)
+ : impl(std::make_unique<Impl>(context, std::move(size), renderbuffer)) {
assert(!size.isEmpty());
}
diff --git a/src/mbgl/util/offscreen_texture.hpp b/src/mbgl/util/offscreen_texture.hpp
index c265700555..7f7e0f0338 100644
--- a/src/mbgl/util/offscreen_texture.hpp
+++ b/src/mbgl/util/offscreen_texture.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <mbgl/map/view.hpp>
#include <mbgl/util/image.hpp>
namespace mbgl {
@@ -10,21 +9,18 @@ class Context;
class Texture;
} // namespace gl
-enum class OffscreenTextureAttachment {
- None,
- Depth,
-};
-
-class OffscreenTexture : public View {
+class OffscreenTexture {
public:
OffscreenTexture(gl::Context&,
- Size size = { 256, 256 },
- OffscreenTextureAttachment type = OffscreenTextureAttachment::None);
- ~OffscreenTexture() override;
+ Size size = { 256, 256 });
+ OffscreenTexture(gl::Context&,
+ Size size,
+ gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&);
+ ~OffscreenTexture();
OffscreenTexture(OffscreenTexture&&);
OffscreenTexture& operator=(OffscreenTexture&&);
- void bind() override;
+ void bind();
PremultipliedImage readStillImage();
diff --git a/src/mbgl/util/std.hpp b/src/mbgl/util/std.hpp
index 974e21329c..1db20e09e5 100644
--- a/src/mbgl/util/std.hpp
+++ b/src/mbgl/util/std.hpp
@@ -8,10 +8,10 @@ namespace mbgl {
namespace util {
template <typename Container, typename ForwardIterator, typename Predicate>
-void erase_if(Container &container, ForwardIterator it, const ForwardIterator end, Predicate pred) {
- while (it != end) {
+void erase_if(Container &container, ForwardIterator it, Predicate pred) {
+ while (it != container.end()) {
if (pred(*it)) {
- container.erase(it++);
+ it = container.erase(it);
} else {
++it;
}
@@ -20,7 +20,7 @@ void erase_if(Container &container, ForwardIterator it, const ForwardIterator en
template <typename Container, typename Predicate>
void erase_if(Container &container, Predicate pred) {
- erase_if(container, container.begin(), container.end(), pred);
+ erase_if(container, container.begin(), pred);
}
} // namespace util
diff --git a/src/mbgl/util/thread_local.hpp b/src/mbgl/util/thread_local.hpp
index 15d4d56524..b0e26356b4 100644
--- a/src/mbgl/util/thread_local.hpp
+++ b/src/mbgl/util/thread_local.hpp
@@ -1,12 +1,8 @@
#pragma once
-#include <mbgl/util/logging.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <cassert>
-#include <stdexcept>
-
-#include <pthread.h>
+#include <memory>
namespace mbgl {
namespace util {
@@ -14,40 +10,20 @@ namespace util {
template <class T>
class ThreadLocal : public noncopyable {
public:
- ThreadLocal() {
- int ret = pthread_key_create(&key, [](void *ptr) {
- delete reinterpret_cast<T *>(ptr);
- });
-
- if (ret) {
- throw std::runtime_error("Failed to init local storage key.");
- }
- }
-
- ~ThreadLocal() {
- if (pthread_key_delete(key)) {
- Log::Error(Event::General, "Failed to delete local storage key.");
- assert(false);
- }
+ ThreadLocal(T* val) {
+ ThreadLocal();
+ set(val);
}
- T* get() {
- auto* ret = reinterpret_cast<T*>(pthread_getspecific(key));
- if (!ret) {
- return nullptr;
- }
+ ThreadLocal();
+ ~ThreadLocal();
- return ret;
- }
-
- void set(T* ptr) {
- if (pthread_setspecific(key, ptr)) {
- throw std::runtime_error("Failed to set local storage.");
- }
- }
+ T* get();
+ void set(T* ptr);
private:
- pthread_key_t key;
+ class Impl;
+ std::unique_ptr<Impl> impl;
};
} // namespace util
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
index b53e91162c..39b562d811 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -126,9 +126,9 @@ std::vector<UnwrappedTileID> tileCover(const Point<double>& tl,
} // namespace
-int32_t coveringZoomLevel(double zoom, SourceType type, uint16_t size) {
+int32_t coveringZoomLevel(double zoom, style::SourceType type, uint16_t size) {
zoom += std::log(util::tileSize / size) / std::log(2);
- if (type == SourceType::Raster || type == SourceType::Video) {
+ if (type == style::SourceType::Raster || type == style::SourceType::Video) {
return ::round(zoom);
} else {
return std::floor(zoom);
@@ -169,5 +169,26 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
z);
}
+// 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 = ::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) - ::fmax(std::min(y1, y2), 0);
+
+ return (maxX - minX + 1) * (maxY - minY + 1);
+}
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
index 2d32d8bf41..b2098b59b8 100644
--- a/src/mbgl/util/tile_cover.hpp
+++ b/src/mbgl/util/tile_cover.hpp
@@ -13,10 +13,13 @@ class LatLngBounds;
namespace util {
-int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize);
+int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize);
std::vector<UnwrappedTileID> tileCover(const TransformState&, int32_t z);
std::vector<UnwrappedTileID> tileCover(const LatLngBounds&, int32_t z);
+// Compute only the count of tiles needed for tileCover
+uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize);
+
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp
new file mode 100644
index 0000000000..60839357d5
--- /dev/null
+++ b/src/mbgl/util/tiny_sdf.cpp
@@ -0,0 +1,105 @@
+#include <mbgl/util/tiny_sdf.hpp>
+
+#include <mbgl/util/math.hpp>
+
+#include <algorithm>
+
+namespace mbgl {
+namespace util {
+
+namespace tinysdf {
+
+static const double INF = 1e20;
+
+// 1D squared distance transform
+void edt1d(std::vector<double>& f,
+ std::vector<double>& d,
+ std::vector<int16_t>& v,
+ std::vector<double>& z,
+ uint32_t n) {
+ v[0] = 0;
+ z[0] = -INF;
+ z[1] = +INF;
+
+ for (uint32_t q = 1, k = 0; q < n; q++) {
+ double s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);
+ while (s <= z[k]) {
+ k--;
+ s = ((f[q] + q * q) - (f[v[k]] + v[k] * v[k])) / (2 * q - 2 * v[k]);
+ }
+ k++;
+ v[k] = q;
+ z[k] = s;
+ z[k + 1] = +INF;
+ }
+
+ for (uint32_t q = 0, k = 0; q < n; q++) {
+ while (z[k + 1] < q) k++;
+ d[q] = (q - v[k]) * (q - v[k]) + f[v[k]];
+ }
+}
+
+
+// 2D Euclidean distance transform by Felzenszwalb & Huttenlocher https://cs.brown.edu/~pff/dt/
+void edt(std::vector<double>& data,
+ uint32_t width,
+ uint32_t height,
+ std::vector<double>& f,
+ std::vector<double>& d,
+ std::vector<int16_t>& v,
+ std::vector<double>& z) {
+ for (uint32_t x = 0; x < width; x++) {
+ for (uint32_t y = 0; y < height; y++) {
+ f[y] = data[y * width + x];
+ }
+ edt1d(f, d, v, z, height);
+ for (uint32_t y = 0; y < height; y++) {
+ data[y * width + x] = d[y];
+ }
+ }
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ f[x] = data[y * width + x];
+ }
+ edt1d(f, d, v, z, width);
+ for (uint32_t x = 0; x < width; x++) {
+ data[y * width + x] = std::sqrt(d[x]);
+ }
+ }
+}
+
+} // namespace tinysdf
+
+AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, double cutoff) {
+ uint32_t size = rasterInput.size.width * rasterInput.size.height;
+ uint32_t maxDimension = std::max(rasterInput.size.width, rasterInput.size.height);
+
+ AlphaImage sdf(rasterInput.size);
+
+ // temporary arrays for the distance transform
+ std::vector<double> gridOuter(size);
+ std::vector<double> gridInner(size);
+ std::vector<double> f(maxDimension);
+ std::vector<double> d(maxDimension);
+ std::vector<double> z(maxDimension + 1);
+ std::vector<int16_t> v(maxDimension);
+
+ for (uint32_t i = 0; i < size; i++) {
+ double a = double(rasterInput.data[i]) / 255; // alpha value
+ gridOuter[i] = a == 1.0 ? 0.0 : a == 0.0 ? tinysdf::INF : std::pow(std::max(0.0, 0.5 - a), 2.0);
+ gridInner[i] = a == 1.0 ? tinysdf::INF : a == 0.0 ? 0.0 : std::pow(std::max(0.0, a - 0.5), 2.0);
+ }
+
+ tinysdf::edt(gridOuter, rasterInput.size.width, rasterInput.size.height, f, d, v, z);
+ tinysdf::edt(gridInner, rasterInput.size.width, rasterInput.size.height, f, d, v, z);
+
+ 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))));
+ }
+
+ return sdf;
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/tiny_sdf.hpp b/src/mbgl/util/tiny_sdf.hpp
new file mode 100644
index 0000000000..33c9280cbd
--- /dev/null
+++ b/src/mbgl/util/tiny_sdf.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/util/image.hpp>
+
+namespace mbgl {
+namespace util {
+
+/*
+ C++ port of https://github.com/mapbox/tiny-sdf, which is in turn based on the
+ Felzenszwalb/Huttenlocher distance transform paper (https://cs.brown.edu/~pff/papers/dt-final.pdf).
+ Note there exists an alternative C++ implementation from the paper’s authors at
+ https://cs.brown.edu/~pff/dt/, which this implementation is not based on.
+
+ Takes an alpha channel raster input and transforms it into an alpha channel
+ Signed Distance Field (SDF) output of the same dimensions.
+*/
+AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, double cutoff);
+
+} // namespace util
+} // namespace mbgl
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/actor/actor.test.cpp b/test/actor/actor.test.cpp
index 3d97469628..967dc152d9 100644
--- a/test/actor/actor.test.cpp
+++ b/test/actor/actor.test.cpp
@@ -281,3 +281,80 @@ TEST(Actor, NonConcurrentMailbox) {
test.invoke(&Test::end);
endedFuture.wait();
}
+
+TEST(Actor, Ask) {
+ // Asking for a result
+
+ struct Test {
+
+ Test(ActorRef<Test>) {}
+
+ int doubleIt(int i) {
+ return i * 2;
+ }
+ };
+
+ ThreadPool pool { 2 };
+ Actor<Test> test(pool);
+
+ auto result = test.ask(&Test::doubleIt, 1);
+
+ ASSERT_TRUE(result.valid());
+
+ auto status = result.wait_for(std::chrono::seconds(1));
+ ASSERT_EQ(std::future_status::ready, status);
+ ASSERT_EQ(2, result.get());
+}
+
+TEST(Actor, AskVoid) {
+ // Ask waits for void methods
+
+ struct Test {
+ bool& executed;
+
+ Test(bool& executed_) : executed(executed_) {
+ }
+
+ void doIt() {
+ executed = true;
+ }
+ };
+
+ ThreadPool pool { 1 };
+ bool executed = false;
+ Actor<Test> actor(pool, executed);
+
+ actor.ask(&Test::doIt).get();
+ EXPECT_TRUE(executed);
+}
+
+TEST(Actor, NoSelfActorRef) {
+ // Not all actors need a reference to self
+
+ // Trivially constructable
+ struct Trivial {};
+
+ ThreadPool pool { 2 };
+ Actor<Trivial> trivial(pool);
+
+
+ // With arguments
+ struct WithArguments {
+ std::promise<void> promise;
+
+ WithArguments(std::promise<void> promise_)
+ : promise(std::move(promise_)) {
+ }
+
+ void receive() {
+ promise.set_value();
+ }
+ };
+
+ std::promise<void> promise;
+ auto future = promise.get_future();
+ Actor<WithArguments> withArguments(pool, std::move(promise));
+
+ withArguments.invoke(&WithArguments::receive);
+ future.wait();
+}
diff --git a/test/actor/actor_ref.test.cpp b/test/actor/actor_ref.test.cpp
index 78721c965e..20aa1c35c1 100644
--- a/test/actor/actor_ref.test.cpp
+++ b/test/actor/actor_ref.test.cpp
@@ -3,12 +3,9 @@
#include <mbgl/test/util.hpp>
-#include <chrono>
-#include <functional>
#include <future>
using namespace mbgl;
-using namespace std::chrono_literals;
TEST(ActorRef, CanOutliveActor) {
// An ActorRef can outlive its actor. Doing does not extend the actor's lifetime.
@@ -40,3 +37,80 @@ TEST(ActorRef, CanOutliveActor) {
EXPECT_TRUE(died);
test.invoke(&Test::receive);
}
+
+TEST(ActorRef, Ask) {
+ // Ask returns a Future eventually returning the result
+
+ struct Test {
+
+ Test(ActorRef<Test>) {}
+
+ int gimme() {
+ return 20;
+ }
+
+ int echo(int i) {
+ return i;
+ }
+ };
+
+ ThreadPool pool { 1 };
+ Actor<Test> actor(pool);
+ ActorRef<Test> ref = actor.self();
+
+ EXPECT_EQ(20, ref.ask(&Test::gimme).get());
+ EXPECT_EQ(30, ref.ask(&Test::echo, 30).get());
+}
+
+TEST(ActorRef, AskVoid) {
+ // Ask waits for void methods
+
+ struct Test {
+ bool& executed;
+
+ Test(bool& executed_) : executed(executed_) {
+ }
+
+ void doIt() {
+ executed = true;
+ }
+ };
+
+ ThreadPool pool { 1 };
+ bool executed = false;
+ Actor<Test> actor(pool, executed);
+ ActorRef<Test> ref = actor.self();
+
+ ref.ask(&Test::doIt).get();
+ EXPECT_TRUE(executed);
+}
+
+TEST(ActorRef, AskOnDestroyedActor) {
+ // Tests behavior when calling ask() after the
+ // Actor has gone away. Should set a exception_ptr.
+
+ struct Test {
+ bool& died;
+
+ Test(ActorRef<Test>, bool& died_) : died(died_) {}
+
+ ~Test() {
+ died = true;
+ }
+
+ int receive() {
+ return 1;
+ }
+ };
+ bool died = false;
+
+ ThreadPool pool { 1 };
+ auto actor = std::make_unique<Actor<Test>>(pool, died);
+ ActorRef<Test> ref = actor->self();
+
+ actor.reset();
+ EXPECT_TRUE(died);
+
+ auto result = ref.ask(&Test::receive);
+ EXPECT_ANY_THROW(result.get());
+}
diff --git a/test/algorithm/generate_clip_ids.test.cpp b/test/algorithm/generate_clip_ids.test.cpp
index 8ca0191b3a..9dc86305af 100644
--- a/test/algorithm/generate_clip_ids.test.cpp
+++ b/test/algorithm/generate_clip_ids.test.cpp
@@ -5,173 +5,153 @@
using namespace mbgl;
struct Renderable {
+ UnwrappedTileID id;
ClipID clip;
- bool used = true;
+ bool used;
+ bool needsClipping;
+
+ Renderable(UnwrappedTileID id_,
+ ClipID clip_,
+ bool used_ = true,
+ bool needsClipping_ = true)
+ : id(std::move(id_)),
+ clip(std::move(clip_)),
+ used(used_),
+ needsClipping(needsClipping_) {}
bool operator==(const Renderable& rhs) const {
- return clip == rhs.clip;
+ return id == rhs.id && clip == rhs.clip;
}
};
::std::ostream& operator<<(::std::ostream& os, const Renderable& rhs) {
- return os << "ClipID(" << rhs.clip << ")";
+ return os << "Renderable{ " << rhs.id << ", " << rhs.clip << " }";
}
-namespace {
-
-// void print(const std::map<UnwrappedTileID, Renderable>& renderables) {
-// std::cout << " EXPECT_EQ(decltype(renderables)({" << std::endl;
-// for (auto& pair : renderables) {
-// std::cout << " { UnwrappedTileID{ " << int(pair.first.canonical.z) << ", "
-// << (int64_t(pair.first.canonical.x) +
-// pair.first.wrap * (1ll << pair.first.canonical.z))
-// << ", " << pair.first.canonical.y << " }, Renderable{ ClipID{ \""
-// << pair.second.clip.mask << "\", \"" << pair.second.clip.reference << "\" } } },"
-// << std::endl;
-// }
-// std::cout << " })," << std::endl;
-// std::cout << " renderables);" << std::endl;
-// }
-
-// void print(const std::map<UnwrappedTileID, ClipID>& stencils) {
-// std::cout << " EXPECT_EQ(decltype(stencils)({" << std::endl;
-// for (auto& pair : stencils) {
-// std::cout << " { UnwrappedTileID{ " << int(pair.first.canonical.z) << ", "
-// << (int64_t(pair.first.canonical.x) +
-// pair.first.wrap * (1ll << pair.first.canonical.z))
-// << ", " << pair.first.canonical.y << " }, ClipID{ \"" << pair.second.mask
-// << "\", \"" << pair.second.reference << "\" } }," << std::endl;
-// }
-// std::cout << " })," << std::endl;
-// std::cout << " stencils);" << std::endl;
-// }
-
-} // end namespace
-
TEST(GenerateClipIDs, ParentAndFourChildren) {
- std::map<UnwrappedTileID, Renderable> renderables{
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ {} } },
+ std::vector<Renderable> renderables{
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, {} },
// All four covering children
- { UnwrappedTileID{ 1, 0, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, 0, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, 1, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, 1, 1 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 1, 0, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 1, 0, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 1, 1, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 1, 1, 1 }, {} },
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables);
+ generator.update<Renderable>({ renderables.begin(), renderables.end() });
EXPECT_EQ(decltype(renderables)({
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ ClipID{ "00000111", "00000001" } } },
- { UnwrappedTileID{ 1, 0, 0 }, Renderable{ ClipID{ "00000111", "00000010" } } },
- { UnwrappedTileID{ 1, 0, 1 }, Renderable{ ClipID{ "00000111", "00000011" } } },
- { UnwrappedTileID{ 1, 1, 0 }, Renderable{ ClipID{ "00000111", "00000100" } } },
- { UnwrappedTileID{ 1, 1, 1 }, Renderable{ ClipID{ "00000111", "00000101" } } },
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, ClipID{ "00000111", "00000001" } },
+ Renderable{ UnwrappedTileID{ 1, 0, 0 }, ClipID{ "00000111", "00000010" } },
+ Renderable{ UnwrappedTileID{ 1, 0, 1 }, ClipID{ "00000111", "00000011" } },
+ Renderable{ UnwrappedTileID{ 1, 1, 0 }, ClipID{ "00000111", "00000100" } },
+ Renderable{ UnwrappedTileID{ 1, 1, 1 }, ClipID{ "00000111", "00000101" } },
}),
renderables);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
// 0/0/0 is missing because it is covered by children.
{ UnwrappedTileID{ 1, 0, 0 }, ClipID{ "00000111", "00000010" } },
{ UnwrappedTileID{ 1, 0, 1 }, ClipID{ "00000111", "00000011" } },
{ UnwrappedTileID{ 1, 1, 0 }, ClipID{ "00000111", "00000100" } },
{ UnwrappedTileID{ 1, 1, 1 }, ClipID{ "00000111", "00000101" } },
}),
- stencils);
+ clipIDs);
}
TEST(GenerateClipIDs, ParentAndFourChildrenNegative) {
- std::map<UnwrappedTileID, Renderable> renderables{
- { UnwrappedTileID{ 1, -2, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, -2, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, -1, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, -1, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 0, -1, 0 }, Renderable{ {} } },
+ std::vector<Renderable> renderables{
+ Renderable{ UnwrappedTileID{ 1, -2, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 1, -2, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 1, -1, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 1, -1, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 0, -1, 0 }, {} },
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables);
+ generator.update<Renderable>({ renderables.begin(), renderables.end() });
EXPECT_EQ(decltype(renderables)({
- { UnwrappedTileID{ 0, -1, 0 }, Renderable{ ClipID{ "00000111", "00000001" } } },
- { UnwrappedTileID{ 1, -2, 0 }, Renderable{ ClipID{ "00000111", "00000010" } } },
- { UnwrappedTileID{ 1, -2, 1 }, Renderable{ ClipID{ "00000111", "00000011" } } },
- { UnwrappedTileID{ 1, -1, 0 }, Renderable{ ClipID{ "00000111", "00000100" } } },
- { UnwrappedTileID{ 1, -1, 1 }, Renderable{ ClipID{ "00000111", "00000101" } } },
+ Renderable{ UnwrappedTileID{ 1, -2, 0 }, ClipID{ "00000111", "00000010" } },
+ Renderable{ UnwrappedTileID{ 1, -2, 1 }, ClipID{ "00000111", "00000011" } },
+ Renderable{ UnwrappedTileID{ 1, -1, 0 }, ClipID{ "00000111", "00000100" } },
+ Renderable{ UnwrappedTileID{ 1, -1, 1 }, ClipID{ "00000111", "00000101" } },
+ Renderable{ UnwrappedTileID{ 0, -1, 0 }, ClipID{ "00000111", "00000001" } },
}),
renderables);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
{ UnwrappedTileID{ 1, -2, 0 }, ClipID{ "00000111", "00000010" } },
{ UnwrappedTileID{ 1, -2, 1 }, ClipID{ "00000111", "00000011" } },
{ UnwrappedTileID{ 1, -1, 0 }, ClipID{ "00000111", "00000100" } },
{ UnwrappedTileID{ 1, -1, 1 }, ClipID{ "00000111", "00000101" } },
}),
- stencils);
+ clipIDs);
}
TEST(GenerateClipIDs, NegativeParentAndMissingLevel) {
- std::map<UnwrappedTileID, Renderable> renderables{
- { UnwrappedTileID{ 1, -1, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, -1, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, -2, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, -1, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, -2, 0 }, Renderable{ {} } },
+ std::vector<Renderable> renderables{
+ Renderable{ UnwrappedTileID{ 1, -1, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 2, -1, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 2, -2, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 2, -1, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 2, -2, 0 }, {} },
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables);
+ generator.update<Renderable>({ renderables.begin(), renderables.end() });
EXPECT_EQ(decltype(renderables)({
- { UnwrappedTileID{ 1, -1, 0 }, Renderable{ ClipID{ "00000111", "00000001" } } },
- { UnwrappedTileID{ 2, -2, 0 }, Renderable{ ClipID{ "00000111", "00000010" } } },
- { UnwrappedTileID{ 2, -2, 1 }, Renderable{ ClipID{ "00000111", "00000011" } } },
- { UnwrappedTileID{ 2, -1, 0 }, Renderable{ ClipID{ "00000111", "00000100" } } },
- { UnwrappedTileID{ 2, -1, 1 }, Renderable{ ClipID{ "00000111", "00000101" } } },
+ Renderable{ UnwrappedTileID{ 1, -1, 0 }, ClipID{ "00000111", "00000001" } },
+ Renderable{ UnwrappedTileID{ 2, -1, 0 }, ClipID{ "00000111", "00000100" } },
+ Renderable{ UnwrappedTileID{ 2, -2, 1 }, ClipID{ "00000111", "00000011" } },
+ Renderable{ UnwrappedTileID{ 2, -1, 1 }, ClipID{ "00000111", "00000101" } },
+ Renderable{ UnwrappedTileID{ 2, -2, 0 }, ClipID{ "00000111", "00000010" } },
}),
renderables);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
{ UnwrappedTileID{ 2, -2, 0 }, ClipID{ "00000111", "00000010" } },
{ UnwrappedTileID{ 2, -2, 1 }, ClipID{ "00000111", "00000011" } },
{ UnwrappedTileID{ 2, -1, 0 }, ClipID{ "00000111", "00000100" } },
{ UnwrappedTileID{ 2, -1, 1 }, ClipID{ "00000111", "00000101" } },
}),
- stencils);
+ clipIDs);
}
TEST(GenerateClipIDs, SevenOnSameLevel) {
- std::map<UnwrappedTileID, Renderable> renderables{
+ std::vector<Renderable> renderables{
// first column
- { UnwrappedTileID{ 2, 0, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 0, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 0, 2 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 2, 0, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 0, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 0, 2 }, {} },
// second column
- { UnwrappedTileID{ 2, 1, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 1, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 1, 2 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 2, 1, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 1, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 1, 2 }, {} },
// third column
- { UnwrappedTileID{ 2, 2, 0 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 2, 2, 0 }, {} },
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables);
+ generator.update<Renderable>({ renderables.begin(), renderables.end() });
EXPECT_EQ(decltype(renderables)({
- { UnwrappedTileID{ 2, 0, 0 }, Renderable{ ClipID{ "00000111", "00000001" } } },
- { UnwrappedTileID{ 2, 0, 1 }, Renderable{ ClipID{ "00000111", "00000010" } } },
- { UnwrappedTileID{ 2, 0, 2 }, Renderable{ ClipID{ "00000111", "00000011" } } },
- { UnwrappedTileID{ 2, 1, 0 }, Renderable{ ClipID{ "00000111", "00000100" } } },
- { UnwrappedTileID{ 2, 1, 1 }, Renderable{ ClipID{ "00000111", "00000101" } } },
- { UnwrappedTileID{ 2, 1, 2 }, Renderable{ ClipID{ "00000111", "00000110" } } },
- { UnwrappedTileID{ 2, 2, 0 }, Renderable{ ClipID{ "00000111", "00000111" } } },
+ Renderable{ UnwrappedTileID{ 2, 0, 0 }, ClipID{ "00000111", "00000001" } },
+ Renderable{ UnwrappedTileID{ 2, 0, 1 }, ClipID{ "00000111", "00000010" } },
+ Renderable{ UnwrappedTileID{ 2, 0, 2 }, ClipID{ "00000111", "00000011" } },
+ Renderable{ UnwrappedTileID{ 2, 1, 0 }, ClipID{ "00000111", "00000100" } },
+ Renderable{ UnwrappedTileID{ 2, 1, 1 }, ClipID{ "00000111", "00000101" } },
+ Renderable{ UnwrappedTileID{ 2, 1, 2 }, ClipID{ "00000111", "00000110" } },
+ Renderable{ UnwrappedTileID{ 2, 2, 0 }, ClipID{ "00000111", "00000111" } },
}),
renderables);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
{ UnwrappedTileID{ 2, 0, 0 }, ClipID{ "00000111", "00000001" } },
{ UnwrappedTileID{ 2, 0, 1 }, ClipID{ "00000111", "00000010" } },
{ UnwrappedTileID{ 2, 0, 2 }, ClipID{ "00000111", "00000011" } },
@@ -180,51 +160,51 @@ TEST(GenerateClipIDs, SevenOnSameLevel) {
{ UnwrappedTileID{ 2, 1, 2 }, ClipID{ "00000111", "00000110" } },
{ UnwrappedTileID{ 2, 2, 0 }, ClipID{ "00000111", "00000111" } },
}),
- stencils);
+ clipIDs);
}
TEST(GenerateClipIDs, MultipleLevels) {
- std::map<UnwrappedTileID, Renderable> renderables{
- { UnwrappedTileID{ 2, 0, 0 }, Renderable{ {} } },
+ std::vector<Renderable> renderables{
+ Renderable{ UnwrappedTileID{ 2, 0, 0 }, {} },
// begin subtiles of (2/0/0)
- { UnwrappedTileID{ 3, 0, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 3, 0, 1 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 3, 0, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 3, 0, 1 }, {} },
// begin subtiles of (3/0/1)
- { UnwrappedTileID{ 4, 0, 2 }, Renderable{ {} } },
- { UnwrappedTileID{ 4, 1, 2 }, Renderable{ {} } },
- { UnwrappedTileID{ 4, 0, 3 }, Renderable{ {} } },
- { UnwrappedTileID{ 4, 1, 3 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 4, 0, 2 }, {} },
+ Renderable{ UnwrappedTileID{ 4, 1, 2 }, {} },
+ Renderable{ UnwrappedTileID{ 4, 0, 3 }, {} },
+ Renderable{ UnwrappedTileID{ 4, 1, 3 }, {} },
// end subtiles of (3/0/1)
- { UnwrappedTileID{ 3, 1, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 3, 1, 1 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 3, 1, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 3, 1, 1 }, {} },
// end subtiles of (2/0/0)
- { UnwrappedTileID{ 2, 1, 0 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 2, 1, 0 }, {} },
// begin subtiles of (2/1/0)
- { UnwrappedTileID{ 3, 2, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 3, 2, 1 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 3, 2, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 3, 2, 1 }, {} },
// end subtiles of (2/1/0)
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables);
+ generator.update<Renderable>({ renderables.begin(), renderables.end() });
ASSERT_EQ(decltype(renderables)({
- { UnwrappedTileID{ 2, 0, 0 }, Renderable{ ClipID{ "00001111", "00000001" } } },
- { UnwrappedTileID{ 2, 1, 0 }, Renderable{ ClipID{ "00001111", "00000010" } } },
- { UnwrappedTileID{ 3, 0, 0 }, Renderable{ ClipID{ "00001111", "00000011" } } },
- { UnwrappedTileID{ 3, 0, 1 }, Renderable{ ClipID{ "00001111", "00000100" } } },
- { UnwrappedTileID{ 3, 1, 0 }, Renderable{ ClipID{ "00001111", "00000101" } } },
- { UnwrappedTileID{ 3, 1, 1 }, Renderable{ ClipID{ "00001111", "00000110" } } },
- { UnwrappedTileID{ 3, 2, 0 }, Renderable{ ClipID{ "00001111", "00000111" } } },
- { UnwrappedTileID{ 3, 2, 1 }, Renderable{ ClipID{ "00001111", "00001000" } } },
- { UnwrappedTileID{ 4, 0, 2 }, Renderable{ ClipID{ "00001111", "00001001" } } },
- { UnwrappedTileID{ 4, 0, 3 }, Renderable{ ClipID{ "00001111", "00001010" } } },
- { UnwrappedTileID{ 4, 1, 2 }, Renderable{ ClipID{ "00001111", "00001011" } } },
- { UnwrappedTileID{ 4, 1, 3 }, Renderable{ ClipID{ "00001111", "00001100" } } },
+ Renderable{ UnwrappedTileID{ 2, 0, 0 }, ClipID{ "00001111", "00000001" } },
+ Renderable{ UnwrappedTileID{ 3, 0, 0 }, ClipID{ "00001111", "00000011" } },
+ Renderable{ UnwrappedTileID{ 3, 0, 1 }, ClipID{ "00001111", "00000100" } },
+ Renderable{ UnwrappedTileID{ 4, 0, 2 }, ClipID{ "00001111", "00001001" } },
+ Renderable{ UnwrappedTileID{ 4, 1, 2 }, ClipID{ "00001111", "00001011" } },
+ Renderable{ UnwrappedTileID{ 4, 0, 3 }, ClipID{ "00001111", "00001010" } },
+ Renderable{ UnwrappedTileID{ 4, 1, 3 }, ClipID{ "00001111", "00001100" } },
+ Renderable{ UnwrappedTileID{ 3, 1, 0 }, ClipID{ "00001111", "00000101" } },
+ Renderable{ UnwrappedTileID{ 3, 1, 1 }, ClipID{ "00001111", "00000110" } },
+ Renderable{ UnwrappedTileID{ 2, 1, 0 }, ClipID{ "00001111", "00000010" } },
+ Renderable{ UnwrappedTileID{ 3, 2, 0 }, ClipID{ "00001111", "00000111" } },
+ Renderable{ UnwrappedTileID{ 3, 2, 1 }, ClipID{ "00001111", "00001000" } },
}),
renderables);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
{ UnwrappedTileID{ 2, 1, 0 }, ClipID{ "00001111", "00000010" } },
{ UnwrappedTileID{ 3, 0, 0 }, ClipID{ "00001111", "00000011" } },
{ UnwrappedTileID{ 3, 1, 0 }, ClipID{ "00001111", "00000101" } },
@@ -236,46 +216,46 @@ TEST(GenerateClipIDs, MultipleLevels) {
{ UnwrappedTileID{ 4, 1, 2 }, ClipID{ "00001111", "00001011" } },
{ UnwrappedTileID{ 4, 1, 3 }, ClipID{ "00001111", "00001100" } },
}),
- stencils);
+ clipIDs);
}
TEST(GenerateClipIDs, Bug206) {
- std::map<UnwrappedTileID, Renderable> renderables{
- { UnwrappedTileID{ 10, 162, 395 }, Renderable{ {} } },
- { UnwrappedTileID{ 10, 162, 396 }, Renderable{ {} } },
- { UnwrappedTileID{ 10, 163, 395 }, Renderable{ {} } },
+ std::vector<Renderable> renderables{
+ Renderable{ UnwrappedTileID{ 10, 162, 395 }, {} },
+ Renderable{ UnwrappedTileID{ 10, 162, 396 }, {} },
+ Renderable{ UnwrappedTileID{ 10, 163, 395 }, {} },
// begin subtiles of (10/163/395)
- { UnwrappedTileID{ 11, 326, 791 }, Renderable{ {} } },
- { UnwrappedTileID{ 12, 654, 1582 }, Renderable{ {} } },
- { UnwrappedTileID{ 12, 654, 1583 }, Renderable{ {} } },
- { UnwrappedTileID{ 12, 655, 1582 }, Renderable{ {} } },
- { UnwrappedTileID{ 12, 655, 1583 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 11, 326, 791 }, {} },
+ Renderable{ UnwrappedTileID{ 12, 654, 1582 }, {} },
+ Renderable{ UnwrappedTileID{ 12, 654, 1583 }, {} },
+ Renderable{ UnwrappedTileID{ 12, 655, 1582 }, {} },
+ Renderable{ UnwrappedTileID{ 12, 655, 1583 }, {} },
// end subtiles of (10/163/395)
- { UnwrappedTileID{ 10, 163, 396 }, Renderable{ {} } },
- { UnwrappedTileID{ 10, 164, 395 }, Renderable{ {} } },
- { UnwrappedTileID{ 10, 164, 396 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 10, 163, 396 }, {} },
+ Renderable{ UnwrappedTileID{ 10, 164, 395 }, {} },
+ Renderable{ UnwrappedTileID{ 10, 164, 396 }, {} },
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables);
+ generator.update<Renderable>({ renderables.begin(), renderables.end() });
EXPECT_EQ(
decltype(renderables)({
- { UnwrappedTileID{ 10, 162, 395 }, Renderable{ ClipID{ "00001111", "00000001" } } },
- { UnwrappedTileID{ 10, 162, 396 }, Renderable{ ClipID{ "00001111", "00000010" } } },
- { UnwrappedTileID{ 10, 163, 395 }, Renderable{ ClipID{ "00001111", "00000011" } } },
- { UnwrappedTileID{ 10, 163, 396 }, Renderable{ ClipID{ "00001111", "00000100" } } },
- { UnwrappedTileID{ 10, 164, 395 }, Renderable{ ClipID{ "00001111", "00000101" } } },
- { UnwrappedTileID{ 10, 164, 396 }, Renderable{ ClipID{ "00001111", "00000110" } } },
- { UnwrappedTileID{ 11, 326, 791 }, Renderable{ ClipID{ "00001111", "00000111" } } },
- { UnwrappedTileID{ 12, 654, 1582 }, Renderable{ ClipID{ "00001111", "00001000" } } },
- { UnwrappedTileID{ 12, 654, 1583 }, Renderable{ ClipID{ "00001111", "00001001" } } },
- { UnwrappedTileID{ 12, 655, 1582 }, Renderable{ ClipID{ "00001111", "00001010" } } },
- { UnwrappedTileID{ 12, 655, 1583 }, Renderable{ ClipID{ "00001111", "00001011" } } },
+ Renderable{ UnwrappedTileID{ 10, 162, 395 }, ClipID{ "00001111", "00000001" } },
+ Renderable{ UnwrappedTileID{ 10, 162, 396 }, ClipID{ "00001111", "00000010" } },
+ Renderable{ UnwrappedTileID{ 10, 163, 395 }, ClipID{ "00001111", "00000011" } },
+ Renderable{ UnwrappedTileID{ 11, 326, 791 }, ClipID{ "00001111", "00000111" } },
+ Renderable{ UnwrappedTileID{ 12, 654, 1582 }, ClipID{ "00001111", "00001000" } },
+ Renderable{ UnwrappedTileID{ 12, 654, 1583 }, ClipID{ "00001111", "00001001" } },
+ Renderable{ UnwrappedTileID{ 12, 655, 1582 }, ClipID{ "00001111", "00001010" } },
+ Renderable{ UnwrappedTileID{ 12, 655, 1583 }, ClipID{ "00001111", "00001011" } },
+ Renderable{ UnwrappedTileID{ 10, 163, 396 }, ClipID{ "00001111", "00000100" } },
+ Renderable{ UnwrappedTileID{ 10, 164, 395 }, ClipID{ "00001111", "00000101" } },
+ Renderable{ UnwrappedTileID{ 10, 164, 396 }, ClipID{ "00001111", "00000110" } },
}),
renderables);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
{ UnwrappedTileID{ 10, 162, 395 }, ClipID{ "00001111", "00000001" } },
{ UnwrappedTileID{ 10, 162, 396 }, ClipID{ "00001111", "00000010" } },
{ UnwrappedTileID{ 10, 163, 395 }, ClipID{ "00001111", "00000011" } },
@@ -288,62 +268,62 @@ TEST(GenerateClipIDs, Bug206) {
{ UnwrappedTileID{ 12, 655, 1582 }, ClipID{ "00001111", "00001010" } },
{ UnwrappedTileID{ 12, 655, 1583 }, ClipID{ "00001111", "00001011" } },
}),
- stencils);
+ clipIDs);
}
TEST(GenerateClipIDs, MultipleSources) {
- std::map<UnwrappedTileID, Renderable> renderables1{
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, 1, 1 }, Renderable{ {} } },
+ std::vector<Renderable> renderables1{
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 1, 1, 1 }, {} },
// Differing children
- { UnwrappedTileID{ 2, 2, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 2, 2 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 2, 2, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 2, 2 }, {} },
};
- std::map<UnwrappedTileID, Renderable> renderables2{
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, 1, 1 }, Renderable{ {} } },
+ std::vector<Renderable> renderables2{
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 1, 1, 1 }, {} },
// Differing children
- { UnwrappedTileID{ 2, 1, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 2, 2 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 2, 1, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 2, 2 }, {} },
};
- std::map<UnwrappedTileID, Renderable> renderables3{
- { UnwrappedTileID{ 1, 0, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, 0, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, 1, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 1, 1, 1 }, Renderable{ {} } },
+ std::vector<Renderable> renderables3{
+ Renderable{ UnwrappedTileID{ 1, 0, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 1, 0, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 1, 1, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 1, 1, 1 }, {} },
// Differing children
- { UnwrappedTileID{ 2, 1, 1 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 2, 1, 1 }, {} },
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables1);
- generator.update(renderables2);
- generator.update(renderables3);
+ generator.update<Renderable>({ renderables1.begin(), renderables1.end() });
+ generator.update<Renderable>({ renderables2.begin(), renderables2.end() });
+ generator.update<Renderable>({ renderables3.begin(), renderables3.end() });
EXPECT_EQ(decltype(renderables1)({
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ ClipID{ "00000111", "00000001" } } },
- { UnwrappedTileID{ 1, 1, 1 }, Renderable{ ClipID{ "00000111", "00000010" } } },
- { UnwrappedTileID{ 2, 2, 1 }, Renderable{ ClipID{ "00000111", "00000011" } } },
- { UnwrappedTileID{ 2, 2, 2 }, Renderable{ ClipID{ "00000111", "00000100" } } },
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, ClipID{ "00000111", "00000001" } },
+ Renderable{ UnwrappedTileID{ 1, 1, 1 }, ClipID{ "00000111", "00000010" } },
+ Renderable{ UnwrappedTileID{ 2, 2, 1 }, ClipID{ "00000111", "00000011" } },
+ Renderable{ UnwrappedTileID{ 2, 2, 2 }, ClipID{ "00000111", "00000100" } },
}),
renderables1);
EXPECT_EQ(decltype(renderables2)({
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ ClipID{ "00011000", "00001000" } } },
- { UnwrappedTileID{ 1, 1, 1 }, Renderable{ ClipID{ "00011111", "00000010" } } },
- { UnwrappedTileID{ 2, 1, 1 }, Renderable{ ClipID{ "00011000", "00010000" } } },
- { UnwrappedTileID{ 2, 2, 2 }, Renderable{ ClipID{ "00011111", "00000100" } } },
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, ClipID{ "00011000", "00001000" } },
+ Renderable{ UnwrappedTileID{ 1, 1, 1 }, ClipID{ "00011111", "00000010" } },
+ Renderable{ UnwrappedTileID{ 2, 1, 1 }, ClipID{ "00011000", "00010000" } },
+ Renderable{ UnwrappedTileID{ 2, 2, 2 }, ClipID{ "00011111", "00000100" } },
}),
renderables2);
EXPECT_EQ(decltype(renderables3)({
- { UnwrappedTileID{ 1, 0, 0 }, Renderable{ ClipID{ "11100000", "00100000" } } },
- { UnwrappedTileID{ 1, 0, 1 }, Renderable{ ClipID{ "11100000", "01000000" } } },
- { UnwrappedTileID{ 1, 1, 0 }, Renderable{ ClipID{ "11100000", "01100000" } } },
- { UnwrappedTileID{ 1, 1, 1 }, Renderable{ ClipID{ "11100000", "10000000" } } },
- { UnwrappedTileID{ 2, 1, 1 }, Renderable{ ClipID{ "11111000", "00010000" } } },
+ Renderable{ UnwrappedTileID{ 1, 0, 0 }, ClipID{ "11100000", "00100000" } },
+ Renderable{ UnwrappedTileID{ 1, 0, 1 }, ClipID{ "11100000", "01000000" } },
+ Renderable{ UnwrappedTileID{ 1, 1, 0 }, ClipID{ "11100000", "01100000" } },
+ Renderable{ UnwrappedTileID{ 1, 1, 1 }, ClipID{ "11100000", "10000000" } },
+ Renderable{ UnwrappedTileID{ 2, 1, 1 }, ClipID{ "11111000", "00010000" } },
}),
renderables3);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
{ UnwrappedTileID{ 1, 0, 0 }, ClipID{ "11111111", "00101001" } },
{ UnwrappedTileID{ 1, 0, 1 }, ClipID{ "11111111", "01001001" } },
{ UnwrappedTileID{ 1, 1, 0 }, ClipID{ "11111111", "01101001" } },
@@ -352,77 +332,78 @@ TEST(GenerateClipIDs, MultipleSources) {
{ UnwrappedTileID{ 2, 2, 1 }, ClipID{ "11111111", "01101011" } },
{ UnwrappedTileID{ 2, 2, 2 }, ClipID{ "11111111", "10000100" } },
}),
- stencils);
+ clipIDs);
}
TEST(GenerateClipIDs, DuplicateIDs) {
- std::map<UnwrappedTileID, Renderable> renderables1{
- { UnwrappedTileID{ 2, 0, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 0, 1 }, Renderable{ {} } },
+ std::vector<Renderable> renderables1{
+ Renderable{ UnwrappedTileID{ 2, 0, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 0, 1 }, {} },
};
- std::map<UnwrappedTileID, Renderable> renderables2{
- { UnwrappedTileID{ 2, 0, 0 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 0, 1 }, Renderable{ {} } },
- { UnwrappedTileID{ 2, 0, 1 }, Renderable{ {} } },
+ std::vector<Renderable> renderables2{
+ Renderable{ UnwrappedTileID{ 2, 0, 0 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 0, 1 }, {} },
+ Renderable{ UnwrappedTileID{ 2, 0, 1 }, {} },
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables1);
- generator.update(renderables2);
+ generator.update<Renderable>({ renderables1.begin(), renderables1.end() });
+ generator.update<Renderable>({ renderables2.begin(), renderables2.end() });
EXPECT_EQ(decltype(renderables1)({
- { UnwrappedTileID{ 2, 0, 0 }, Renderable{ ClipID{ "00000011", "00000001" } } },
- { UnwrappedTileID{ 2, 0, 1 }, Renderable{ ClipID{ "00000011", "00000010" } } },
+ Renderable{ UnwrappedTileID{ 2, 0, 0 }, ClipID{ "00000011", "00000001" } },
+ Renderable{ UnwrappedTileID{ 2, 0, 1 }, ClipID{ "00000011", "00000010" } },
}),
renderables1);
EXPECT_EQ(decltype(renderables2)({
- { UnwrappedTileID{ 2, 0, 0 }, Renderable{ ClipID{ "00000011", "00000001" } } },
- { UnwrappedTileID{ 2, 0, 1 }, Renderable{ ClipID{ "00000011", "00000010" } } },
+ Renderable{ UnwrappedTileID{ 2, 0, 0 }, ClipID{ "00000011", "00000001" } },
+ Renderable{ UnwrappedTileID{ 2, 0, 1 }, ClipID{ "00000011", "00000010" } },
+ Renderable{ UnwrappedTileID{ 2, 0, 1 }, ClipID{ "00000011", "00000010" } },
}),
renderables2);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
{ UnwrappedTileID{ 2, 0, 0 }, ClipID{ "00000011", "00000001" } },
{ UnwrappedTileID{ 2, 0, 1 }, ClipID{ "00000011", "00000010" } },
}),
- stencils);
+ clipIDs);
}
TEST(GenerateClipIDs, SecondSourceHasParentOfFirstSource) {
- std::map<UnwrappedTileID, Renderable> renderables1{
- { UnwrappedTileID{ 1, 0, 0 }, Renderable{ {} } },
+ std::vector<Renderable> renderables1{
+ Renderable{ UnwrappedTileID{ 1, 0, 0 }, {} },
};
- std::map<UnwrappedTileID, Renderable> renderables2{
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ {} } },
+ std::vector<Renderable> renderables2{
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, {} },
// Same as in renderables1, but has a parent that it knocks out.
- { UnwrappedTileID{ 1, 0, 0 }, Renderable{ {} } },
+ Renderable{ UnwrappedTileID{ 1, 0, 0 }, {} },
};
- std::map<UnwrappedTileID, Renderable> renderables3{
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ {} } },
+ std::vector<Renderable> renderables3{
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, {} },
};
algorithm::ClipIDGenerator generator;
- generator.update(renderables1);
- generator.update(renderables2);
- generator.update(renderables3);
+ generator.update<Renderable>({ renderables1.begin(), renderables1.end() });
+ generator.update<Renderable>({ renderables2.begin(), renderables2.end() });
+ generator.update<Renderable>({ renderables3.begin(), renderables3.end() });
EXPECT_EQ(decltype(renderables1)({
- { UnwrappedTileID{ 1, 0, 0 }, Renderable{ ClipID{ "00000001", "00000001" } } },
+ Renderable{ UnwrappedTileID{ 1, 0, 0 }, ClipID{ "00000001", "00000001" } },
}),
renderables1);
EXPECT_EQ(decltype(renderables2)({
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ ClipID{ "00000010", "00000010" } } },
- { UnwrappedTileID{ 1, 0, 0 }, Renderable{ ClipID{ "00000011", "00000001" } } },
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, ClipID{ "00000010", "00000010" } },
+ Renderable{ UnwrappedTileID{ 1, 0, 0 }, ClipID{ "00000011", "00000001" } },
}),
renderables2);
EXPECT_EQ(decltype(renderables3)({
- { UnwrappedTileID{ 0, 0, 0 }, Renderable{ ClipID{ "00000100", "00000100" } } },
+ Renderable{ UnwrappedTileID{ 0, 0, 0 }, ClipID{ "00000100", "00000100" } },
}),
renderables3);
- const auto stencils = generator.getStencils();
- EXPECT_EQ(decltype(stencils)({
+ const auto clipIDs = generator.getClipIDs();
+ EXPECT_EQ(decltype(clipIDs)({
{ UnwrappedTileID{ 0, 0, 0 }, ClipID{ "00000110", "00000110" } },
{ UnwrappedTileID{ 1, 0, 0 }, ClipID{ "00000111", "00000101" } },
}),
- stencils);
+ clipIDs);
}
diff --git a/test/algorithm/mock.hpp b/test/algorithm/mock.hpp
index d87f55343b..b8eb020105 100644
--- a/test/algorithm/mock.hpp
+++ b/test/algorithm/mock.hpp
@@ -26,7 +26,7 @@ struct MockBucket {};
struct MockTileData {
MockTileData(const mbgl::OverscaledTileID& tileID_) : tileID(tileID_) {}
- bool hasTriedOptional() const {
+ bool hasTriedCache() const {
return triedOptional;
}
diff --git a/test/algorithm/update_renderables.test.cpp b/test/algorithm/update_renderables.test.cpp
index af90d262de..7d6a0fcb13 100644
--- a/test/algorithm/update_renderables.test.cpp
+++ b/test/algorithm/update_renderables.test.cpp
@@ -23,10 +23,10 @@ struct GetTileDataAction {
};
std::ostream& operator<<(std::ostream& os, const GetTileDataAction& action) {
- return os << "GetTileDataAction{ { " << int(action.tileID.overscaledZ) << ", { "
+ return os << "GetTileDataAction{ { " << int(action.tileID.overscaledZ) << ", " << int(action.tileID.wrap) << ", { "
<< int(action.tileID.canonical.z) << ", " << action.tileID.canonical.x << ", "
<< action.tileID.canonical.y << " } }, "
- << (action.found == Found ? "Found" : "NotFound") << " }";
+ << (action.found == Found ? "Found" : "NotFound") << " }\n";
}
struct CreateTileDataAction {
@@ -38,14 +38,14 @@ struct CreateTileDataAction {
};
std::ostream& operator<<(std::ostream& os, const CreateTileDataAction& action) {
- return os << "CreateTileDataAction{ { " << int(action.tileID.overscaledZ) << ", { "
+ return os << "CreateTileDataAction{ { " << int(action.tileID.overscaledZ) << ", " << int(action.tileID.wrap) << ", { "
<< int(action.tileID.canonical.z) << ", " << action.tileID.canonical.x << ", "
- << action.tileID.canonical.y << " } } }";
+ << action.tileID.canonical.y << " } } }\n";
}
struct RetainTileDataAction {
const OverscaledTileID tileID;
- const Resource::Necessity necessity;
+ const TileNecessity necessity;
bool operator==(const RetainTileDataAction& rhs) const {
return tileID == rhs.tileID && necessity == rhs.necessity;
@@ -53,10 +53,10 @@ struct RetainTileDataAction {
};
std::ostream& operator<<(std::ostream& os, const RetainTileDataAction& action) {
- return os << "RetainTileDataAction{ { " << int(action.tileID.overscaledZ) << ", { "
+ return os << "RetainTileDataAction{ { " << int(action.tileID.overscaledZ) << ", " << int(action.tileID.wrap) << ", { "
<< int(action.tileID.canonical.z) << ", " << action.tileID.canonical.x << ", "
<< action.tileID.canonical.y << " } }, "
- << (action.necessity == Resource::Necessity::Required ? "Required" : "Optional") << " }";
+ << (action.necessity == TileNecessity::Required ? "Required" : "Optional") << " }\n";
}
struct RenderTileAction {
@@ -76,7 +76,7 @@ std::ostream& operator<<(std::ostream& os, const RenderTileAction& action) {
<< int(action.tileData.tileID.overscaledZ) << "_"
<< int(action.tileData.tileID.canonical.z) << "_"
<< action.tileData.tileID.canonical.x << "_" << action.tileData.tileID.canonical.y
- << " }";
+ << " }\n";
}
using ActionLogEntry =
@@ -100,12 +100,14 @@ auto createTileDataFn(ActionLog& log, T& dataTiles) {
};
}
+template <typename = int>
auto retainTileDataFn(ActionLog& log) {
- return [&](auto& tileData, Resource::Necessity necessity) {
+ return [&](auto& tileData, TileNecessity necessity) {
log.emplace_back(RetainTileDataAction{ tileData.tileID, necessity });
};
}
+template <typename = int>
auto renderTileFn(ActionLog& log) {
return [&](const auto& id, auto& tileData) {
log.emplace_back(RenderTileAction{ id, tileData });
@@ -129,8 +131,8 @@ TEST(UpdateRenderables, SingleTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
}),
log);
@@ -140,8 +142,8 @@ TEST(UpdateRenderables, SingleTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
}),
log);
@@ -152,39 +154,39 @@ TEST(UpdateRenderables, SingleTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // missing ideal tile
- CreateTileDataAction{ { 1, { 1, 0, 1 } } }, // create ideal tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // four child tiles
- GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile
-
- GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 1, 0, { 1, 0, 1 } } }, // create ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 2 } }, NotFound }, // four child tiles
+ GetTileDataAction{ { 2, 0, { 2, 0, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile
+
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile
}),
log);
// Mark the created tile as having the optional request tried.
log.clear();
- source.dataTiles[{ 1, { 1, 0, 1 } }]->triedOptional = true;
+ source.dataTiles[{ 1, 0, { 1, 0, 1 } }]->triedOptional = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // missing ideal tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // four child tiles
- GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile
- CreateTileDataAction{ { 0, { 0, 0, 0 } } }, // load parent tile
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
-
- GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // found ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // missing ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 2 } }, NotFound }, // four child tiles
+ GetTileDataAction{ { 2, 0, { 2, 0, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile
+ CreateTileDataAction{ { 0, 0, { 0, 0, 0 } } }, // load parent tile
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
+
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // found ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile
}),
log);
@@ -196,12 +198,12 @@ TEST(UpdateRenderables, SingleTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // newly added tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // newly added tile
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
- GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render found tile
}),
log);
@@ -215,22 +217,22 @@ TEST(UpdateRenderables, SingleTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // found tile, not ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // four child tiles
- GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // found tile, not ready
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // four child tiles
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
// optional parent tile was already created before, but is not renderable
- GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
- GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, // render ideal tile
}),
log);
@@ -241,16 +243,16 @@ TEST(UpdateRenderables, SingleTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // found tile, now ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // found tile, now ready
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, //
- GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, //
- GetTileDataAction{ { 1, { 1, 1, 1 } }, Found }, // ideal tile
- RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, Found }, // ideal tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 1, 1 }, *tile_1_1_1_1 }, //
}),
log);
@@ -274,30 +276,30 @@ TEST(UpdateRenderables, UseParentTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // missing ideal tile
- CreateTileDataAction{ { 1, { 1, 0, 1 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // child tile
- GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent found!
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 1, 0, { 1, 0, 1 } } }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 2 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, 0, { 2, 0, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent found!
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // render parent
- GetTileDataAction{ { 1, { 1, 1, 0 } }, NotFound }, // missing ideal tile
- CreateTileDataAction{ { 1, { 1, 1, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 1, 1 } }, NotFound }, // missing tile
- CreateTileDataAction{ { 1, { 1, 1, 1 } } }, //
- RetainTileDataAction{ { 1, { 1, 1, 1 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 2, 2 } }, NotFound }, // child tile
- GetTileDataAction{ { 2, { 2, 2, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 3, 2 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 3, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 1, 0 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 1, 0, { 1, 1, 0 } } }, //
+ RetainTileDataAction{ { 1, 0, { 1, 1, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 2, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, 0, { 2, 2, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 3, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 3, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, NotFound }, // missing tile
+ CreateTileDataAction{ { 1, 0, { 1, 1, 1 } } }, //
+ RetainTileDataAction{ { 1, 0, { 1, 1, 1 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 2, 2 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, 0, { 2, 2, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 3, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 3, 3 } }, NotFound }, // ...
}),
log);
}
@@ -317,34 +319,34 @@ TEST(UpdateRenderables, DontUseWrongParentTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // missing ideal tile
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // parent tile, missing
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // parent tile, missing
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing
}),
log);
// Now mark the created tile as having the optional request tried.
log.clear();
- source.dataTiles[{ 2, { 2, 0, 0 } }]->triedOptional = true;
+ source.dataTiles[{ 2, 0, { 2, 0, 0 } }]->triedOptional = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // non-ready ideal tile
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // parent tile, missing
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, // find optional parent
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // non-ready ideal tile
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // parent tile, missing
+ CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, // find optional parent
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // parent tile, missing
}),
log);
@@ -354,26 +356,26 @@ TEST(UpdateRenderables, DontUseWrongParentTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // non-ready ideal tile
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // non-ready ideal tile
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
// this tile was added by the previous invocation of updateRenderables
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // parent tile not ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // missing parent tile
-
- GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, // missing ideal tile
- CreateTileDataAction{ { 2, { 2, 2, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 2, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 3, { 3, 4, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 3, { 3, 4, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 5, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 5, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // found parent tile
- RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // parent tile not ready
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // missing parent tile
+
+ GetTileDataAction{ { 2, 0, { 2, 2, 0 } }, NotFound }, // missing ideal tile
+ CreateTileDataAction{ { 2, 0, { 2, 2, 0 } } }, //
+ RetainTileDataAction{ { 2, 0, { 2, 2, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 3, 4, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 3, 0, { 3, 4, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 5, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 5, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 1, 0 } }, Found }, // found parent tile
+ RetainTileDataAction{ { 1, 0, { 1, 1, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 1, 1, 0 }, *tile_1_1_1_0 }, // render parent tile
}),
log);
@@ -399,14 +401,14 @@ TEST(UpdateRenderables, UseParentTileWhenChildNotReady) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // found, but not ready
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 2 } }, NotFound }, // child tile
- GetTileDataAction{ { 2, { 2, 0, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 2 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 3 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, ready
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // found, but not ready
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 2 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, 0, { 2, 0, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 2 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 3 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile, ready
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, // render parent tile
}),
log);
@@ -417,8 +419,8 @@ TEST(UpdateRenderables, UseParentTileWhenChildNotReady) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // found and ready
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // found and ready
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, // render ideal tile
}),
log);
@@ -444,19 +446,19 @@ TEST(UpdateRenderables, UseOverlappingParentTile) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile not found
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile found
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ideal tile not found
+ CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile found
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
- GetTileDataAction{ { 1, { 1, 0, 1 } }, Found }, // ideal tile found
- RetainTileDataAction{ { 1, { 1, 0, 1 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, Found }, // ideal tile found
+ RetainTileDataAction{ { 1, 0, { 1, 0, 1 } }, TileNecessity::Required }, //
RenderTileAction{ { 1, 0, 1 }, *tile_1_1_0_1 }, //
}),
log);
@@ -480,17 +482,17 @@ TEST(UpdateRenderables, UseChildTiles) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 0);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ideal tile, missing
- CreateTileDataAction{ { 0, { 0, 0, 0 } } }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // child tile found
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 0, 0, { 0, 0, 0 } } }, //
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // child tile found
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, // render child tile
- GetTileDataAction{ { 1, { 1, 0, 1 } }, NotFound }, // child tile not found
- GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // child tile found
- RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 1 } }, NotFound }, // child tile not found
+ GetTileDataAction{ { 1, 0, { 1, 1, 0 } }, Found }, // child tile found
+ RetainTileDataAction{ { 1, 0, { 1, 1, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 1, 1, 0 }, *tile_1_1_1_0 }, // render child tile
- GetTileDataAction{ { 1, { 1, 1, 1 } }, NotFound }, // child tile not found
+ GetTileDataAction{ { 1, 0, { 1, 1, 1 } }, NotFound }, // child tile not found
// no parent tile of 0 to consider
}),
log);
@@ -514,17 +516,17 @@ TEST(UpdateRenderables, PreferChildTiles) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile, not found
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ideal tile, not found
+ CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, // child tile, not found
- GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, // child tile, not found
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile, found
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -537,19 +539,19 @@ TEST(UpdateRenderables, PreferChildTiles) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // ideal tile, not ready
// ideal tile was added in previous invocation, but is not yet ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ...
- RetainTileDataAction{ { 2, { 2, 0, 1 } }, Resource::Necessity::Optional }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, Found }, // ...
+ RetainTileDataAction{ { 2, 0, { 2, 0, 1 } }, TileNecessity::Optional }, // ...
RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, // child tile, not found
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, // child tile, not found
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile, found
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -560,21 +562,21 @@ TEST(UpdateRenderables, PreferChildTiles) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // ideal tile, not ready
// ideal tile was added in first invocation, but is not yet ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ...
- RetainTileDataAction{ { 2, { 2, 0, 1 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, Found }, // ...
+ RetainTileDataAction{ { 2, 0, { 2, 0, 1 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, Found }, // ...
- RetainTileDataAction{ { 2, { 2, 1, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 2, 0, { 2, 1, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 1, 0 }, *tile_2_2_1_0 }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, // child tile, not found
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile, found
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, // child tile, not found
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile, found
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -586,20 +588,20 @@ TEST(UpdateRenderables, PreferChildTiles) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // ideal tile, not ready
// ideal tile was added in first invocation, but is not yet ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile, found
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile, found
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, Found }, // ...
- RetainTileDataAction{ { 2, { 2, 0, 1 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, Found }, // ...
+ RetainTileDataAction{ { 2, 0, { 2, 0, 1 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 1 }, *tile_2_2_0_1 }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, Found }, // ...
- RetainTileDataAction{ { 2, { 2, 1, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 2, 0, { 2, 1, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 1, 0 }, *tile_2_2_1_0 }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, Found }, // ...
- RetainTileDataAction{ { 2, { 2, 1, 1 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, Found }, // ...
+ RetainTileDataAction{ { 2, 0, { 2, 1, 1 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 1, 1 }, *tile_2_2_1_1 }, //
}),
log);
@@ -624,17 +626,17 @@ TEST(UpdateRenderables, UseParentAndChildTiles) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile, missing
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // child tile
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // child tile
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -645,14 +647,14 @@ TEST(UpdateRenderables, UseParentAndChildTiles) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, // parent tile
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, // parent tile
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
}),
log);
@@ -675,13 +677,13 @@ TEST(UpdateRenderables, DontUseTilesLowerThanMinzoom) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, //
- GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, //
- GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, //
// no requests for zoom 1 tiles
}),
log);
@@ -705,58 +707,58 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) {
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(
ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children!
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children!
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, //
}),
log);
// Mark the created tile as having tried the optional request.
log.clear();
- source.dataTiles[{ 2, { 2, 0, 0 } }]->triedOptional = true;
+ source.dataTiles[{ 2, 0, { 2, 0, 0 } }]->triedOptional = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(
ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // ideal tile, missing
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children!
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, //
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // ideal tile, missing
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, // overzoomed tile, not children!
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, //
+ CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, //
}),
log);
// Only add a non-overzoomed ("parent") tile at first.
log.clear();
- auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } });
+ auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, 0, { 2, 0, 0 } });
tile_2_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
- CreateTileDataAction{ { 3, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, // ideal tile, missing
+ CreateTileDataAction{ { 3, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
}),
log);
// Then add the overzoomed tile matching the zoom level we're rendering.
log.clear();
- auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } });
+ auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, 0, { 2, 0, 0 } });
tile_3_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
}),
log);
@@ -766,26 +768,26 @@ TEST(UpdateRenderables, UseOverzoomedTileAfterMaxzoom) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
}),
log);
// Now remove the best match.
log.clear();
- source.dataTiles.erase(OverscaledTileID{ 2, { 2, 0, 0 } });
+ source.dataTiles.erase(OverscaledTileID{ 2, 0, { 2, 0, 0 } });
tile_2_2_0_0 = nullptr;
// Use the overzoomed tile even though it doesn't match the zoom level.
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // use overzoomed tile!
- RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, //
+ CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // use overzoomed tile!
+ RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
}),
log);
@@ -803,69 +805,69 @@ TEST(UpdateRenderables, AscendToNonOverzoomedTiles) {
source.idealTiles.emplace(UnwrappedTileID{ 2, 0, 0 });
// Add a matching overzoomed tile and make sure it gets selected.
- auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, { 2, 0, 0 } });
+ auto tile_3_2_0_0 = source.createTileData(OverscaledTileID{ 3, 0, { 2, 0, 0 } });
tile_3_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
RenderTileAction{ { 2, 0, 0 }, *tile_3_2_0_0 }, //
}),
log);
// Then, swap it with a non-overzoomed tile.
log.clear();
- source.dataTiles.erase(OverscaledTileID{ 3, { 2, 0, 0 } });
+ source.dataTiles.erase(OverscaledTileID{ 3, 0, { 2, 0, 0 } });
tile_3_2_0_0 = nullptr;
- auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, { 2, 0, 0 } });
+ auto tile_2_2_0_0 = source.createTileData(OverscaledTileID{ 2, 0, { 2, 0, 0 } });
tile_2_2_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, NotFound }, //
- CreateTileDataAction{ { 3, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, // prefer using a child first
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, NotFound }, //
+ CreateTileDataAction{ { 3, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, // prefer using a child first
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 2, 0, 0 }, *tile_2_2_0_0 }, //
}),
log);
// Then, swap it with a parent tile.
log.clear();
- source.dataTiles.erase(OverscaledTileID{ 2, { 2, 0, 0 } });
+ source.dataTiles.erase(OverscaledTileID{ 2, 0, { 2, 0, 0 } });
tile_2_2_0_0 = nullptr;
- auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, { 1, 0, 0 } });
+ auto tile_1_1_0_0 = source.createTileData(OverscaledTileID{ 1, 0, { 1, 0, 0 } });
tile_1_1_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, //
}),
log);
// Now, mark the created tile as found.
log.clear();
- source.dataTiles[{ 3, { 2, 0, 0 } }]->triedOptional = true;
+ source.dataTiles[{ 3, 0, { 2, 0, 0 } }]->triedOptional = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 3);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 3, { 2, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 3, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 4, { 2, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 2, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 3, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 4, 0, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, //
+ CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 1, 0, 0 }, *tile_1_1_0_0 }, //
}),
log);
@@ -885,60 +887,60 @@ TEST(UpdateRenderables, DoNotAscendMultipleTimesIfNotFound) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 8);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 8, { 8, 0, 0 } }, NotFound }, // ideal tile
- CreateTileDataAction{ { 8, { 8, 0, 0 } } }, //
- RetainTileDataAction{ { 8, { 8, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 9, { 9, 0, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 9, { 9, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 9, { 9, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 9, { 9, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // ascent
- GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
-
- GetTileDataAction{ { 8, { 8, 1, 0 } }, NotFound }, // ideal tile
- CreateTileDataAction{ { 8, { 8, 1, 0 } } }, //
- RetainTileDataAction{ { 8, { 8, 1, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 9, { 9, 2, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 9, { 9, 2, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 9, { 9, 3, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 9, { 9, 3, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 8, 0, { 8, 0, 0 } }, NotFound }, // ideal tile
+ CreateTileDataAction{ { 8, 0, { 8, 0, 0 } } }, //
+ RetainTileDataAction{ { 8, 0, { 8, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 9, 0, { 9, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 9, 0, { 9, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, 0, { 9, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, 0, { 9, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // ascent
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
+
+ GetTileDataAction{ { 8, 0, { 8, 1, 0 } }, NotFound }, // ideal tile
+ CreateTileDataAction{ { 8, 0, { 8, 1, 0 } } }, //
+ RetainTileDataAction{ { 8, 0, { 8, 1, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 9, 0, { 9, 2, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 9, 0, { 9, 2, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, 0, { 9, 3, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, 0, { 9, 3, 1 } }, NotFound }, // ...
// no second ascent to 0
}),
log);
// Now add a mid-level tile that stops the ascent
log.clear();
- auto tile_4_0_0_0 = source.createTileData(OverscaledTileID{ 4, { 4, 0, 0 } });
+ auto tile_4_0_0_0 = source.createTileData(OverscaledTileID{ 4, 0, { 4, 0, 0 } });
tile_4_0_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 8);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 8, { 8, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 8, { 8, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 9, { 9, 0, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 9, { 9, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 9, { 9, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 9, { 9, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // ascent
- GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // stops ascent
- RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 8, 0, { 8, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 8, 0, { 8, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 9, 0, { 9, 0, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 9, 0, { 9, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, 0, { 9, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, 0, { 9, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // ascent
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // stops ascent
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 4, 0, 0 }, *tile_4_0_0_0 }, //
- GetTileDataAction{ { 8, { 8, 1, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 8, { 8, 1, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 9, { 9, 2, 0 } }, NotFound }, // child tile
- GetTileDataAction{ { 9, { 9, 2, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 9, { 9, 3, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 9, { 9, 3, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 8, 0, { 8, 1, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 8, 0, { 8, 1, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 9, 0, { 9, 2, 0 } }, NotFound }, // child tile
+ GetTileDataAction{ { 9, 0, { 9, 2, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, 0, { 9, 3, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 9, 0, { 9, 3, 1 } }, NotFound }, // ...
// no second ascent to 0
}),
log);
@@ -960,15 +962,15 @@ TEST(UpdateRenderables, DontRetainUnusedNonIdealTiles) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 2);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 2, { 2, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 3, { 3, 0, 1 } }, NotFound }, //
- GetTileDataAction{ { 3, { 3, 1, 0 } }, NotFound }, //
- GetTileDataAction{ { 3, { 3, 1, 1 } }, NotFound }, //
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // parent tile, not ready
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 3, 0, { 3, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 3, 0, { 3, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, Found }, // parent tile, not ready
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, //
}),
log);
}
@@ -981,56 +983,54 @@ TEST(UpdateRenderables, WrappedTiles) {
auto retainTileData = retainTileDataFn(log);
auto renderTile = renderTileFn(log);
- source.idealTiles.emplace(UnwrappedTileID{ 1, -1, 0 });
- source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 });
- source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 0 });
- source.idealTiles.emplace(UnwrappedTileID{ 1, 2, 0 });
+ source.idealTiles.emplace(UnwrappedTileID{ 1, -1, 0 }); // 'wrap' -> -1
+ source.idealTiles.emplace(UnwrappedTileID{ 1, 0, 0 }); // 'wrap' -> 0
+ source.idealTiles.emplace(UnwrappedTileID{ 1, 1, 0 }); // 'wrap' -> 0
+ source.idealTiles.emplace(UnwrappedTileID{ 1, 2, 0 }); // 'wrap' -> 1
- auto tile_0_0_0_0 = source.createTileData(OverscaledTileID{ 0, { 0, 0, 0 } });
+ auto tile_0_0_0_0 = source.createTileData(OverscaledTileID{ 0, 0, { 0, 0, 0 } });
tile_0_0_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 1);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 1, { 1, 1, 0 } }, NotFound }, // ideal tile 1/-1/0
- CreateTileDataAction{ { 1, { 1, 1, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
- RenderTileAction{ { 0, -1, 0 }, *tile_0_0_0_0 }, //
-
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ideal tile 1/0/0
- CreateTileDataAction{ { 1, { 1, 0, 0 } } }, //
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 1, -1, { 1, 1, 0 } }, NotFound }, // ideal tile 1/-1/0 (wrapped to -1)
+ CreateTileDataAction{ { 1, -1, { 1, 1, 0 } } }, //
+ RetainTileDataAction{ { 1, -1, { 1, 1, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, -1, { 2, 2, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, -1, { 2, 2, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, -1, { 2, 3, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, -1, { 2, 3, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, -1, { 0, 0, 0 } }, NotFound }, // { 0, 0, 0 } exists, but not the version wrapped to -1
+
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ideal tile 1/0/0
+ CreateTileDataAction{ { 1, 0, { 1, 0, 0 } } }, //
+ RetainTileDataAction{ { 1, 0, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, Found }, //
+ RetainTileDataAction{ { 0, 0, { 0, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 0, 0, 0 }, *tile_0_0_0_0 }, //
- GetTileDataAction{ { 1, { 1, 1, 0 } }, Found }, // ideal tile 1/1/0
- RetainTileDataAction{ { 1, { 1, 1, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 2, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 2, 1 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 3, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 3, 1 } }, NotFound }, //
+ GetTileDataAction{ { 1, 0, { 1, 1, 0 } }, NotFound }, // ideal tile 1/1/0, doesn't match 1/-/1/0
+ CreateTileDataAction{ { 1, 0, { 1, 1, 0 } } },
+ RetainTileDataAction{ { 1, 0, { 1, 1, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 0, { 2, 2, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 2, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 3, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 0, { 2, 3, 1 } }, NotFound }, //
// do not ascent; 0/0/0 has been rendered already for 1/0/0
- GetTileDataAction{ { 1, { 1, 0, 0 } }, Found }, // ideal tile 1/2/0
- RetainTileDataAction{ { 1, { 1, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 0, 1 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 1, 0 } }, NotFound }, //
- GetTileDataAction{ { 2, { 2, 1, 1 } }, NotFound }, //
- GetTileDataAction{ { 0, { 0, 0, 0 } }, Found }, //
- RetainTileDataAction{ { 0, { 0, 0, 0 } }, Resource::Necessity::Optional }, //
- RenderTileAction{ { 0, 1, 0 }, *tile_0_0_0_0 }, //
+ GetTileDataAction{ { 1, 1, { 1, 0, 0 } }, NotFound }, // ideal tile 1/2/0 (wrapped to 1)
+ CreateTileDataAction{ { 1, 1, { 1, 0, 0 } } },
+ RetainTileDataAction{ { 1, 1, { 1, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 2, 1, { 2, 0, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 1, { 2, 0, 1 } }, NotFound }, //
+ GetTileDataAction{ { 2, 1, { 2, 1, 0 } }, NotFound }, //
+ GetTileDataAction{ { 2, 1, { 2, 1, 1 } }, NotFound }, //
+ GetTileDataAction{ { 0, 1, { 0, 0, 0 } }, NotFound }, // { 0, 0, 0 } exists, but not the version wrapped to -1
}),
log);
}
@@ -1048,19 +1048,19 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, NotFound }, // ideal tile, not found
- CreateTileDataAction{ { 6, { 6, 0, 0 } } }, //
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
- GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent
- GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, NotFound }, // ideal tile, not found
+ CreateTileDataAction{ { 6, 0, { 6, 0, 0 } } }, //
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
}),
log);
@@ -1069,41 +1069,41 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
- GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent
- GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
}),
log);
// Mark next level has having tried optional.
log.clear();
- source.dataTiles[{ 6, { 6, 0, 0 } }]->triedOptional = true;
+ source.dataTiles[{ 6, 0, { 6, 0, 0 } }]->triedOptional = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
- GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent
- CreateTileDataAction{ { 5, { 5, 0, 0 } } }, //
- RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent
+ CreateTileDataAction{ { 5, 0, { 5, 0, 0 } } }, //
+ RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
}),
log);
@@ -1112,116 +1112,116 @@ TEST(UpdateRenderables, RepeatedRenderWithMissingOptionals) {
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
- GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
- RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
}),
log);
// Mark next level has having tried optional.
log.clear();
- source.dataTiles[{ 5, { 5, 0, 0 } }]->triedOptional = true;
+ source.dataTiles[{ 5, 0, { 5, 0, 0 } }]->triedOptional = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
- GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
- RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
- CreateTileDataAction{ { 4, { 4, 0, 0 } } }, //
- RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 4, 0, { 4, 0, 0 } } }, //
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
}),
log);
// Mark next level has having tried optional.
log.clear();
- source.dataTiles[{ 4, { 4, 0, 0 } }]->triedOptional = true;
+ source.dataTiles[{ 4, 0, { 4, 0, 0 } }]->triedOptional = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
- GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
- RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ...
- RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
- CreateTileDataAction{ { 3, { 3, 0, 0 } } }, //
- RetainTileDataAction{ { 3, { 3, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 3, 0, { 3, 0, 0 } } }, //
+ RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
}),
log);
// Mark next level has having tried optional.
log.clear();
- source.dataTiles[{ 3, { 3, 0, 0 } }]->triedOptional = true;
+ source.dataTiles[{ 3, 0, { 3, 0, 0 } }]->triedOptional = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
- GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
- RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ...
- RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, Found }, // ...
- RetainTileDataAction{ { 3, { 3, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- CreateTileDataAction{ { 2, { 2, 0, 0 } } }, //
- RetainTileDataAction{ { 2, { 2, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ CreateTileDataAction{ { 2, 0, { 2, 0, 0 } } }, //
+ RetainTileDataAction{ { 2, 0, { 2, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
}),
log);
// Mark as found
log.clear();
- auto tile_3_3_0_0 = source.dataTiles[{ 3, { 3, 0, 0 } }].get();
+ auto tile_3_3_0_0 = source.dataTiles[{ 3, 0, { 3, 0, 0 } }].get();
tile_3_3_0_0->renderable = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not ready
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 7, 0, 0 } }, NotFound }, // children
- GetTileDataAction{ { 7, { 7, 0, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 7, { 7, 1, 1 } }, NotFound }, // ...
- GetTileDataAction{ { 5, { 5, 0, 0 } }, Found }, // ascent
- RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 4, { 4, 0, 0 } }, Found }, // ...
- RetainTileDataAction{ { 4, { 4, 0, 0 } }, Resource::Necessity::Optional }, //
- GetTileDataAction{ { 3, { 3, 0, 0 } }, Found }, // ...
- RetainTileDataAction{ { 3, { 3, 0, 0 } }, Resource::Necessity::Optional }, //
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not ready
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 7, 0, 0 } }, NotFound }, // children
+ GetTileDataAction{ { 7, 0, { 7, 0, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 7, 0, { 7, 1, 1 } }, NotFound }, // ...
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, Found }, // ascent
+ RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 4, 0, { 4, 0, 0 } }, TileNecessity::Optional }, //
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, Found }, // ...
+ RetainTileDataAction{ { 3, 0, { 3, 0, 0 } }, TileNecessity::Optional }, //
RenderTileAction{ { 3, 0, 0 }, *tile_3_3_0_0 }, //
}),
log);
@@ -1238,24 +1238,24 @@ TEST(UpdateRenderables, LoadRequiredIfIdealTileCantBeFound) {
source.zoomRange.max = 6;
source.idealTiles.emplace(UnwrappedTileID{ 6, 0, 0 });
- auto tile_6_6_0_0 = source.createTileData(OverscaledTileID{ 6, { 6, 0, 0 } });
+ auto tile_6_6_0_0 = source.createTileData(OverscaledTileID{ 6, 0, { 6, 0, 0 } });
tile_6_6_0_0->triedOptional = true;
tile_6_6_0_0->loaded = true;
algorithm::updateRenderables(getTileData, createTileData, retainTileData, renderTile,
source.idealTiles, source.zoomRange, 6);
EXPECT_EQ(ActionLog({
- GetTileDataAction{ { 6, { 6, 0, 0 } }, Found }, // ideal tile, not found
- RetainTileDataAction{ { 6, { 6, 0, 0 } }, Resource::Necessity::Required }, //
- GetTileDataAction{ { 7, { 6, 0, 0 } }, NotFound }, // overzoomed child
- GetTileDataAction{ { 5, { 5, 0, 0 } }, NotFound }, // ascent
- CreateTileDataAction{ { 5, { 5, 0, 0 } } },
- RetainTileDataAction{ { 5, { 5, 0, 0 } }, Resource::Necessity::Required },
- GetTileDataAction{ { 4, { 4, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 3, { 3, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 2, { 2, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 1, { 1, 0, 0 } }, NotFound }, // ...
- GetTileDataAction{ { 0, { 0, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 6, 0, { 6, 0, 0 } }, Found }, // ideal tile, not found
+ RetainTileDataAction{ { 6, 0, { 6, 0, 0 } }, TileNecessity::Required }, //
+ GetTileDataAction{ { 7, 0, { 6, 0, 0 } }, NotFound }, // overzoomed child
+ GetTileDataAction{ { 5, 0, { 5, 0, 0 } }, NotFound }, // ascent
+ CreateTileDataAction{ { 5, 0, { 5, 0, 0 } } },
+ RetainTileDataAction{ { 5, 0, { 5, 0, 0 } }, TileNecessity::Required },
+ GetTileDataAction{ { 4, 0, { 4, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 3, 0, { 3, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 2, 0, { 2, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 1, 0, { 1, 0, 0 } }, NotFound }, // ...
+ GetTileDataAction{ { 0, 0, { 0, 0, 0 } }, NotFound }, // ...
}),
log);
}
diff --git a/test/algorithm/update_tile_masks.test.cpp b/test/algorithm/update_tile_masks.test.cpp
new file mode 100644
index 0000000000..3c698eb0cd
--- /dev/null
+++ b/test/algorithm/update_tile_masks.test.cpp
@@ -0,0 +1,132 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/algorithm/update_tile_masks.hpp>
+
+using namespace mbgl;
+
+namespace {
+
+class MaskedRenderable {
+public:
+ MaskedRenderable(const UnwrappedTileID& id_, TileMask&& mask_)
+ : id(id_), mask(std::move(mask_)) {
+ }
+
+ UnwrappedTileID id;
+ TileMask mask;
+ bool used = true;
+
+ void setMask(TileMask&& mask_) {
+ mask = std::move(mask_);
+ }
+};
+
+bool operator==(const MaskedRenderable& lhs, const MaskedRenderable& rhs) {
+ return lhs.id == rhs.id && lhs.mask == rhs.mask;
+}
+
+::std::ostream& operator<<(::std::ostream& os, const MaskedRenderable& rhs) {
+ os << "MaskedRenderable{ " << rhs.id << ", { ";
+ bool first = true;
+ for (auto& id : rhs.mask) {
+ if (!first) {
+ os << ", ";
+ } else {
+ first = false;
+ }
+ os << id;
+ }
+ return os << " } }";
+}
+
+} // namespace
+
+void validate(const std::vector<MaskedRenderable> expected) {
+ std::vector<MaskedRenderable> actual = expected;
+ std::for_each(actual.begin(), actual.end(),
+ [](auto& renderable) { renderable.mask.clear(); });
+ algorithm::updateTileMasks<MaskedRenderable>({ actual.begin(), actual.end() });
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(UpdateTileMasks, NoChildren) {
+ validate({
+ MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+
+ validate({
+ MaskedRenderable{ UnwrappedTileID{ 4, 3, 8 }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+
+ validate({
+ MaskedRenderable{ UnwrappedTileID{ 1, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 1, 1, 1 }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+
+ validate({
+ MaskedRenderable{ UnwrappedTileID{ 1, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 2, 2, 3 }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+}
+
+TEST(UpdateTileMasks, ParentAndFourChildren) {
+ validate({
+ // Mask is empty (== not rendered!) because we have four covering children.
+ MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 }, {} },
+ // All four covering children
+ MaskedRenderable{ UnwrappedTileID{ 1, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 1, 0, 1 }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 1, 1, 0 }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 1, 1, 1 }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+}
+
+TEST(UpdateTileMasks, OneChild) {
+ validate({
+ MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 },
+ // Only render the three children that aren't covering the other tile.
+ { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 },
+ CanonicalTileID{ 1, 1, 1 } } },
+ MaskedRenderable{ UnwrappedTileID{ 1, 0, 0 }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+}
+
+TEST(UpdateTileMasks, Complex) {
+ validate({
+ MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 },
+ { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 },
+ CanonicalTileID{ 2, 2, 3 }, CanonicalTileID{ 2, 3, 2 },
+ CanonicalTileID{ 3, 6, 7 }, CanonicalTileID{ 3, 7, 6 } } },
+ MaskedRenderable{ UnwrappedTileID{ 0, { 1, 0, 0 } }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 0, { 2, 2, 2 } }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 0, { 3, 7, 7 } }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 0, { 3, 6, 6 } }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+
+ validate({
+ MaskedRenderable{ UnwrappedTileID{ 0, 0, 0 },
+ { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 },
+ CanonicalTileID{ 1, 1, 1 }, CanonicalTileID{ 2, 0, 0 },
+ CanonicalTileID{ 2, 0, 1 }, CanonicalTileID{ 2, 1, 0 },
+ CanonicalTileID{ 3, 2, 3 }, CanonicalTileID{ 3, 3, 2 },
+ CanonicalTileID{ 3, 3, 3 }, CanonicalTileID{ 4, 4, 5 },
+ CanonicalTileID{ 4, 5, 4 }, CanonicalTileID{ 4, 5, 5 } } },
+ MaskedRenderable{ UnwrappedTileID{ 4, 4, 4 }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+
+ validate({
+ MaskedRenderable{ UnwrappedTileID{ 12, 1028, 1456 },
+ { CanonicalTileID{ 1, 1, 1 }, CanonicalTileID{ 2, 3, 0 },
+ CanonicalTileID{ 2, 3, 1 } } },
+ MaskedRenderable{ UnwrappedTileID{ 13, 2056, 2912 },
+ { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 },
+ CanonicalTileID{ 1, 1, 1 } } },
+ MaskedRenderable{ UnwrappedTileID{ 13, 2056, 2913 },
+ { CanonicalTileID{ 1, 0, 0 }, CanonicalTileID{ 1, 1, 0 },
+ CanonicalTileID{ 1, 1, 1 } } },
+ MaskedRenderable{ UnwrappedTileID{ 14, 4112, 5824 }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 14, 4112, 5827 }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 14, 4114, 5824 }, { CanonicalTileID{ 0, 0, 0 } } },
+ MaskedRenderable{ UnwrappedTileID{ 14, 4114, 5825 }, { CanonicalTileID{ 0, 0, 0 } } },
+ });
+}
diff --git a/test/api/annotations.test.cpp b/test/api/annotations.test.cpp
index 7594d5ed73..07257851ac 100644
--- a/test/api/annotations.test.cpp
+++ b/test/api/annotations.test.cpp
@@ -6,12 +6,11 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.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>
using namespace mbgl;
@@ -28,16 +27,16 @@ std::unique_ptr<style::Image> namedMarker(const std::string& name) {
class AnnotationTest {
public:
util::RunLoop loop;
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view { backend.getContext() };
StubFileSource fileSource;
ThreadPool threadPool { 4 };
- Map map { backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still };
+ float pixelRatio { 1 };
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
+ Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
+ threadPool, MapMode::Static};
void checkRendering(const char * name) {
test::checkImage(std::string("test/fixtures/annotations/") + name,
- test::render(map, view), 0.0002, 0.1);
+ frontend.render(map), 0.0002, 0.1);
}
};
@@ -51,7 +50,7 @@ TEST(Annotations, SymbolAnnotation) {
test.map.addAnnotation(SymbolAnnotation { Point<double>(0, 0), "default_marker" });
test.checkRendering("point_annotation");
-// auto size = test.view.getSize();
+// auto size = test.frontend.getSize();
// auto screenBox = ScreenBox { {}, { double(size.width), double(size.height) } };
// for (uint8_t zoom = test.map.getMinZoom(); zoom <= test.map.getMaxZoom(); ++zoom) {
// test.map.setZoom(zoom);
@@ -155,7 +154,7 @@ TEST(Annotations, AddMultiple) {
test.map.addAnnotationImage(namedMarker("default_marker"));
test.map.addAnnotation(SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" });
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
test.map.addAnnotation(SymbolAnnotation { Point<double> { 10, 0 }, "default_marker" });
test.checkRendering("add_multiple");
@@ -165,7 +164,7 @@ TEST(Annotations, NonImmediateAdd) {
AnnotationTest test;
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
Polygon<double> polygon = { {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} };
FillAnnotation annotation { polygon };
@@ -183,7 +182,7 @@ TEST(Annotations, UpdateSymbolAnnotationGeometry) {
test.map.addAnnotationImage(namedMarker("flipped_marker"));
AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" });
test.checkRendering("update_point");
@@ -197,7 +196,7 @@ TEST(Annotations, UpdateSymbolAnnotationIcon) {
test.map.addAnnotationImage(namedMarker("flipped_marker"));
AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { 0, 0 }, "flipped_marker" });
test.checkRendering("update_icon");
@@ -213,7 +212,7 @@ TEST(Annotations, UpdateLineAnnotationGeometry) {
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID line = test.map.addAnnotation(annotation);
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
annotation.geometry = LineString<double> {{ { 0, 0 }, { -45, -45 } }};
test.map.updateAnnotation(line, annotation);
@@ -230,7 +229,7 @@ TEST(Annotations, UpdateLineAnnotationStyle) {
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID line = test.map.addAnnotation(annotation);
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
annotation.color = Color::green();
annotation.width = { 2 };
@@ -247,7 +246,7 @@ TEST(Annotations, UpdateFillAnnotationGeometry) {
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID fill = test.map.addAnnotation(annotation);
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
annotation.geometry = Polygon<double> { {{ { 0, 0 }, { 0, 45 }, { 45, 0 } }} };
test.map.updateAnnotation(fill, annotation);
@@ -264,7 +263,7 @@ TEST(Annotations, UpdateFillAnnotationStyle) {
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID fill = test.map.addAnnotation(annotation);
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
annotation.color = Color::green();
test.map.updateAnnotation(fill, annotation);
@@ -278,7 +277,7 @@ TEST(Annotations, RemovePoint) {
test.map.addAnnotationImage(namedMarker("default_marker"));
AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
test.map.removeAnnotation(point);
test.checkRendering("remove_point");
@@ -295,7 +294,7 @@ TEST(Annotations, RemoveShape) {
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID shape = test.map.addAnnotation(annotation);
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
test.map.removeAnnotation(shape);
test.checkRendering("remove_shape");
@@ -307,7 +306,7 @@ TEST(Annotations, ImmediateRemoveShape) {
test.map.removeAnnotation(test.map.addAnnotation(LineAnnotation { LineString<double>() }));
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
}
TEST(Annotations, SwitchStyle) {
@@ -317,7 +316,7 @@ TEST(Annotations, SwitchStyle) {
test.map.addAnnotationImage(namedMarker("default_marker"));
test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
test.checkRendering("switch_style");
@@ -330,7 +329,7 @@ TEST(Annotations, ReaddImage) {
test.map.addAnnotationImage(namedMarker("default_marker"));
test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
test.map.addAnnotationImage(std::make_unique<style::Image>("default_marker", namedImage("flipped_marker"), 1.0));
test.checkRendering("readd_image");
@@ -344,14 +343,14 @@ TEST(Annotations, QueryRenderedFeatures) {
test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 50 }, "default_marker" });
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
- auto features = test.map.queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 }));
+ auto features = test.frontend.getRenderer()->queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 }));
EXPECT_EQ(features.size(), 1u);
EXPECT_TRUE(!!features[0].id);
EXPECT_EQ(*features[0].id, uint64_t(0));
- auto features2 = test.map.queryRenderedFeatures(test.map.pixelForLatLng({ 50, 0 }));
+ auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(test.map.pixelForLatLng({ 50, 0 }));
EXPECT_EQ(features2.size(), 1u);
EXPECT_TRUE(!!features2[0].id);
EXPECT_EQ(*features2[0].id, uint64_t(1));
@@ -360,15 +359,15 @@ TEST(Annotations, QueryRenderedFeatures) {
TEST(Annotations, QueryFractionalZoomLevels) {
AnnotationTest test;
- auto viewSize = test.view.getSize();
+ auto viewSize = test.frontend.getSize();
auto box = ScreenBox { {}, { double(viewSize.width), double(viewSize.height) } };
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
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" }));
}
}
@@ -376,8 +375,8 @@ TEST(Annotations, QueryFractionalZoomLevels) {
test.map.setLatLngZoom({ 5, 5 }, 0);
for (uint16_t zoomSteps = 10; zoomSteps <= 20; ++zoomSteps) {
test.map.setZoom(zoomSteps / 10.0);
- test::render(test.map, test.view);
- auto features = test.map.queryRenderedFeatures(box);
+ test.frontend.render(test.map);
+ auto features = test.frontend.getRenderer()->queryRenderedFeatures(box);
// Filter out repeated features.
// See 'edge-cases/null-island' query-test for reference.
@@ -392,7 +391,7 @@ TEST(Annotations, QueryFractionalZoomLevels) {
TEST(Annotations, VisibleFeatures) {
AnnotationTest test;
- auto viewSize = test.view.getSize();
+ auto viewSize = test.frontend.getSize();
auto box = ScreenBox { {}, { double(viewSize.width), double(viewSize.height) } };
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
@@ -400,17 +399,17 @@ 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" }));
}
}
// Change bearing *after* adding annotations causes them to be reordered.
test.map.setBearing(45);
- test::render(test.map, test.view);
+ test.frontend.render(test.map);
- auto features = test.map.queryRenderedFeatures(box, {});
+ auto features = test.frontend.getRenderer()->queryRenderedFeatures(box, {});
auto sortID = [](const Feature& lhs, const Feature& rhs) { return lhs.id < rhs.id; };
auto sameID = [](const Feature& lhs, const Feature& rhs) { return lhs.id == rhs.id; };
std::sort(features.begin(), features.end(), sortID);
@@ -419,8 +418,8 @@ TEST(Annotations, VisibleFeatures) {
test.map.setBearing(0);
test.map.setZoom(4);
- test::render(test.map, test.view);
- features = test.map.queryRenderedFeatures(box);
+ test.frontend.render(test.map);
+ features = test.frontend.getRenderer()->queryRenderedFeatures(box);
std::sort(features.begin(), features.end(), sortID);
features.erase(std::unique(features.begin(), features.end(), sameID), features.end());
EXPECT_EQ(features.size(), ids.size());
@@ -460,3 +459,19 @@ TEST(Annotations, DebugSparse) {
test.checkRendering("debug_sparse");
}
+
+TEST(Annotations, ChangeMaxZoom) {
+ AnnotationTest test;
+
+ LineString<double> line = {{ { 0, 0 }, { 45, 45 }, { 30, 0 } }};
+ LineAnnotation annotation { line };
+ annotation.color = Color::red();
+ annotation.width = { 5 };
+
+ test.map.setMaxZoom(6);
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.addAnnotation(annotation);
+ test.map.setMaxZoom(14);
+ test.map.setZoom(test.map.getMaxZoom());
+ test.checkRendering("line_annotation_max_zoom");
+}
diff --git a/test/api/api_misuse.test.cpp b/test/api/api_misuse.test.cpp
index 54cde8d9b5..363958451b 100644
--- a/test/api/api_misuse.test.cpp
+++ b/test/api/api_misuse.test.cpp
@@ -3,9 +3,8 @@
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/exception.hpp>
@@ -22,15 +21,14 @@ TEST(API, RenderWithoutCallback) {
util::RunLoop loop;
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view { backend.getContext(), { 128, 512 } };
StubFileSource fileSource;
ThreadPool threadPool(4);
+ float pixelRatio { 1 };
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
- std::unique_ptr<Map> map =
- std::make_unique<Map>(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
- map->renderStill(view, nullptr);
+ auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(),
+ pixelRatio, fileSource, threadPool, MapMode::Static);
+ map->renderStill(nullptr);
// Force Map thread to join.
map.reset();
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 2b1138a1d3..eb1d7e0d3a 100644
--- a/test/api/custom_layer.test.cpp
+++ b/test/api/custom_layer.test.cpp
@@ -2,11 +2,9 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.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>
@@ -87,13 +85,12 @@ public:
TEST(CustomLayer, Basic) {
util::RunLoop loop;
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view { backend.getContext() };
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
ThreadPool threadPool(4);
-
- Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
+ 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);
map.getStyle().addLayer(std::make_unique<CustomLayer>(
@@ -113,5 +110,5 @@ TEST(CustomLayer, Basic) {
layer->setFillColor(Color{ 1.0, 1.0, 0.0, 1.0 });
map.getStyle().addLayer(std::move(layer));
- test::checkImage("test/fixtures/custom_layer/basic", test::render(map, view), 0.0006, 0.1);
+ test::checkImage("test/fixtures/custom_layer/basic", frontend.render(map), 0.0006, 0.1);
}
diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp
index 0b6d75ec4f..c67ff9064c 100644
--- a/test/api/query.test.cpp
+++ b/test/api/query.test.cpp
@@ -1,7 +1,4 @@
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/test/stub_file_source.hpp>
#include <mbgl/test/util.hpp>
@@ -11,6 +8,8 @@
#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/style/source.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
using namespace mbgl;
using namespace mbgl::style;
@@ -24,16 +23,16 @@ public:
map.getStyle().addImage(std::make_unique<style::Image>("test-icon",
decodeImage(util::read_file("test/fixtures/sprites/default_marker.png")), 1.0));
- test::render(map, view);
+ frontend.render(map);
}
util::RunLoop loop;
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view { backend.getContext() };
StubFileSource fileSource;
ThreadPool threadPool { 4 };
- Map map { backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still };
+ float pixelRatio { 1 };
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
+ Map map { frontend, MapObserver::nullObserver(), frontend.getSize(), pixelRatio, fileSource,
+ threadPool, MapMode::Static};
};
} // end namespace
@@ -41,10 +40,10 @@ public:
TEST(Query, QueryRenderedFeatures) {
QueryTest test;
- auto features1 = test.map.queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 }));
+ auto features1 = test.frontend.getRenderer()->queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 }));
EXPECT_EQ(features1.size(), 4u);
- auto features2 = test.map.queryRenderedFeatures(test.map.pixelForLatLng({ 9, 9 }));
+ auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(test.map.pixelForLatLng({ 9, 9 }));
EXPECT_EQ(features2.size(), 0u);
}
@@ -53,16 +52,16 @@ TEST(Query, QueryRenderedFeaturesFilterLayer) {
auto zz = test.map.pixelForLatLng({ 0, 0 });
- auto features1 = test.map.queryRenderedFeatures(zz, {{{ "layer1"}}, {}});
+ auto features1 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{{ "layer1"}}, {}});
EXPECT_EQ(features1.size(), 1u);
- auto features2 = test.map.queryRenderedFeatures(zz, {{{ "layer1", "layer2" }}, {}});
+ auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{{ "layer1", "layer2" }}, {}});
EXPECT_EQ(features2.size(), 2u);
- auto features3 = test.map.queryRenderedFeatures(zz, {{{ "foobar" }}, {}});
+ auto features3 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{{ "foobar" }}, {}});
EXPECT_EQ(features3.size(), 0u);
- auto features4 = test.map.queryRenderedFeatures(zz, {{{ "foobar", "layer3" }}, {}});
+ auto features4 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{{ "foobar", "layer3" }}, {}});
EXPECT_EQ(features4.size(), 1u);
}
@@ -72,22 +71,22 @@ TEST(Query, QueryRenderedFeaturesFilter) {
auto zz = test.map.pixelForLatLng({ 0, 0 });
const EqualsFilter eqFilter = { "key1", std::string("value1") };
- auto features1 = test.map.queryRenderedFeatures(zz, {{}, { eqFilter }});
+ auto features1 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{}, { eqFilter }});
EXPECT_EQ(features1.size(), 1u);
const IdentifierNotEqualsFilter idNotEqFilter = { std::string("feature1") };
- auto features2 = test.map.queryRenderedFeatures(zz, {{{ "layer4" }}, { idNotEqFilter }});
+ auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{{ "layer4" }}, { idNotEqFilter }});
EXPECT_EQ(features2.size(), 0u);
const GreaterThanFilter gtFilter = { "key2", 1.0 };
- auto features3 = test.map.queryRenderedFeatures(zz, {{ }, { gtFilter }});
+ auto features3 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{ }, { gtFilter }});
EXPECT_EQ(features3.size(), 1u);
}
TEST(Query, QuerySourceFeatures) {
QueryTest test;
- auto features1 = test.map.querySourceFeatures("source3");
+ auto features1 = test.frontend.getRenderer()->querySourceFeatures("source3");
EXPECT_EQ(features1.size(), 1u);
}
@@ -95,15 +94,15 @@ TEST(Query, QuerySourceFeaturesOptionValidation) {
QueryTest test;
// GeoJSONSource, doesn't require a layer id
- auto features = test.map.querySourceFeatures("source3");
+ auto features = test.frontend.getRenderer()->querySourceFeatures("source3");
ASSERT_EQ(features.size(), 1u);
// VectorSource, requires a layer id
- features = test.map.querySourceFeatures("source5");
+ features = test.frontend.getRenderer()->querySourceFeatures("source5");
ASSERT_EQ(features.size(), 0u);
// RasterSource, not supported
- features = test.map.querySourceFeatures("source6");
+ features = test.frontend.getRenderer()->querySourceFeatures("source6");
ASSERT_EQ(features.size(), 0u);
}
@@ -111,15 +110,15 @@ TEST(Query, QuerySourceFeaturesFilter) {
QueryTest test;
const EqualsFilter eqFilter = { "key1", std::string("value1") };
- auto features1 = test.map.querySourceFeatures("source4", {{}, { eqFilter }});
+ auto features1 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { eqFilter }});
EXPECT_EQ(features1.size(), 1u);
const IdentifierNotEqualsFilter idNotEqFilter = { std::string("feature1") };
- auto features2 = test.map.querySourceFeatures("source4", {{}, { idNotEqFilter }});
+ auto features2 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { idNotEqFilter }});
EXPECT_EQ(features2.size(), 0u);
const GreaterThanFilter gtFilter = { "key2", 1.0 };
- auto features3 = test.map.querySourceFeatures("source4", {{}, { gtFilter }});
+ auto features3 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { gtFilter }});
EXPECT_EQ(features3.size(), 1u);
}
diff --git a/test/api/recycle_map.cpp b/test/api/recycle_map.cpp
new file mode 100644
index 0000000000..ca6abac8c1
--- /dev/null
+++ b/test/api/recycle_map.cpp
@@ -0,0 +1,58 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/storage/online_file_source.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+
+TEST(API, RecycleMapUpdateImages) {
+ util::RunLoop loop;
+
+ StubFileSource fileSource;
+ ThreadPool threadPool(4);
+ float pixelRatio { 1 };
+
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
+ auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(),
+ pixelRatio, fileSource, threadPool, MapMode::Static);
+
+ EXPECT_TRUE(map);
+
+ auto loadStyle = [&](auto markerName, auto markerPath) {
+ auto source = std::make_unique<GeoJSONSource>("geometry");
+ source->setGeoJSON({ Point<double> { 0, 0 } });
+
+ auto layer = std::make_unique<SymbolLayer>("geometry", "geometry");
+ layer->setIconImage({ markerName });
+
+ map->getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ map->getStyle().addSource(std::move(source));
+ map->getStyle().addLayer(std::move(layer));
+ map->getStyle().addImage(std::make_unique<style::Image>(markerName, decodeImage(util::read_file(markerPath)), 1.0));
+ };
+
+ // default marker
+
+ loadStyle("default_marker", "test/fixtures/sprites/default_marker.png");
+ test::checkImage("test/fixtures/recycle_map/default_marker", frontend.render(*map), 0.0006, 0.1);
+
+ // flipped marker
+
+ loadStyle("flipped_marker", "test/fixtures/sprites/flipped_marker.png");
+ test::checkImage("test/fixtures/recycle_map/flipped_marker", frontend.render(*map), 0.0006, 0.1);
+}
diff --git a/test/api/render_missing.test.cpp b/test/api/render_missing.test.cpp
deleted file mode 100644
index 2e0c4401f4..0000000000
--- a/test/api/render_missing.test.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include <mbgl/test/util.hpp>
-#include <mbgl/test/fixture_log_observer.hpp>
-
-#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
-#include <mbgl/util/default_thread_pool.hpp>
-#include <mbgl/storage/default_file_source.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/util/io.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/style/style.hpp>
-
-#include <future>
-
-#if TEST_HAS_SERVER
-#define TEST_REQUIRES_SERVER(name) name
-#else
-#define TEST_REQUIRES_SERVER(name) DISABLED_ ## name
-#endif
-
-TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) {
- using namespace mbgl;
-
- util::RunLoop loop;
-
- const auto style = util::read_file("test/fixtures/api/water_missing_tiles.json");
-
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view { backend.getContext(), { 256, 512 } };
- DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
- ThreadPool threadPool(4);
-
- Log::setObserver(std::make_unique<FixtureLogObserver>());
-
- Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
-
- std::string message;
-
- // This host does not respond (== connection error).
- // Are you seeing this test fail? Make sure you don't have a server running on port 3001!
- map.getStyle().loadJSON(style);
- map.renderStill(view, [&](std::exception_ptr err) {
- ASSERT_TRUE(err.operator bool());
- try {
- std::rethrow_exception(err);
- } catch (const std::exception& ex) {
- message = ex.what();
- EXPECT_TRUE(message.find("onnect") != std::string::npos); // [C|c]onnect
- }
- loop.stop();
- });
-
- loop.run();
-
- auto observer = Log::removeObserver();
- auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
- EXPECT_EQ(1u, flo->count(FixtureLog::Message(
- EventSeverity::Error, Event::Style, -1,
- std::string("Failed to load tile 0/0/0=>0 for source mapbox: " + message))));
- auto unchecked = flo->unchecked();
- EXPECT_TRUE(unchecked.empty()) << unchecked;
-}
diff --git a/test/api/repeated_render.test.cpp b/test/api/repeated_render.test.cpp
deleted file mode 100644
index b6565dbf23..0000000000
--- a/test/api/repeated_render.test.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include <mbgl/test/util.hpp>
-#include <mbgl/test/fixture_log_observer.hpp>
-
-#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
-#include <mbgl/util/default_thread_pool.hpp>
-#include <mbgl/storage/default_file_source.hpp>
-#include <mbgl/util/image.hpp>
-#include <mbgl/util/io.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/style/style.hpp>
-
-#include <future>
-
-using namespace mbgl;
-
-
-TEST(API, RepeatedRender) {
-
- util::RunLoop loop;
-
- const auto style = util::read_file("test/fixtures/api/water.json");
-
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view { backend.getContext(), { 256, 512 } };
- DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
- ThreadPool threadPool(4);
-
- Log::setObserver(std::make_unique<FixtureLogObserver>());
-
- Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
-
- {
- map.getStyle().loadJSON(style);
- PremultipliedImage result;
- map.renderStill(view, [&](std::exception_ptr) {
- result = view.readStillImage();
- });
-
- while (!result.valid()) {
- loop.runOnce();
- }
-
- ASSERT_EQ(256u, result.size.width);
- ASSERT_EQ(512u, result.size.height);
-#if !TEST_READ_ONLY
- util::write_file("test/fixtures/api/1.png", encodePNG(result));
-#endif
- }
-
- {
- map.getStyle().loadJSON(style);
- PremultipliedImage result;
- map.renderStill(view, [&](std::exception_ptr) {
- result = view.readStillImage();
- });
-
- while (!result.valid()) {
- loop.runOnce();
- }
-
- ASSERT_EQ(256u, result.size.width);
- ASSERT_EQ(512u, result.size.height);
-#if !TEST_READ_ONLY
- util::write_file("test/fixtures/api/2.png", encodePNG(result));
-#endif
- }
-
- auto observer = Log::removeObserver();
- auto flo = dynamic_cast<FixtureLogObserver*>(observer.get());
- auto unchecked = flo->unchecked();
- EXPECT_TRUE(unchecked.empty()) << unchecked;
-}
diff --git a/test/api/zoom_history.cpp b/test/api/zoom_history.cpp
new file mode 100644
index 0000000000..df9b6ff2a3
--- /dev/null
+++ b/test/api/zoom_history.cpp
@@ -0,0 +1,71 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/storage/online_file_source.hpp>
+#include <mbgl/style/function/camera_function.hpp>
+#include <mbgl/style/function/exponential_stops.hpp>
+#include <mbgl/style/image.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/sources/geojson_source.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/exception.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(API, ZoomHistory) {
+ util::RunLoop loop;
+
+ StubFileSource fileSource;
+ ThreadPool threadPool(4);
+ float pixelRatio { 1 };
+
+ HeadlessFrontend frontend { pixelRatio, fileSource, threadPool };
+ auto map = std::make_unique<Map>(frontend, MapObserver::nullObserver(), frontend.getSize(),
+ pixelRatio, fileSource, threadPool, MapMode::Static);
+
+ EXPECT_TRUE(map);
+
+ map->getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+
+ auto source = std::make_unique<GeoJSONSource>("source");
+ source->setGeoJSON({ LineString<double> { { 45, -45 }, { -45, 45 } } });
+ map->getStyle().addSource(std::move(source));
+
+ auto layer = std::make_unique<LineLayer>("layer-1", "source");
+ layer->setLineWidth(CameraFunction<float> { ExponentialStops<float> {{ { 0, 8 }, { 1, 16 } }} });
+ layer->setLineDasharray({ std::vector<float> { 1, 1 } });
+ layer->setLineColor( { Color::black() } );
+ map->getStyle().addLayer(std::move(layer));
+
+ layer = std::make_unique<LineLayer>("layer-2", "source");
+ layer->setLineWidth(CameraFunction<float> { ExponentialStops<float> {{ { 0, 4 }, { 1, 8 } }} });
+ layer->setLineDasharray({ std::vector<float> { 2, 2 } });
+ layer->setLineColor( { Color::red() } );
+ map->getStyle().addLayer(std::move(layer));
+
+ // ZoomHistory.lastIntegerZoom is 1.
+ map->setZoom(1.0);
+ frontend.render(*map);
+
+ map->setZoom(0.0);
+ frontend.render(*map);
+
+ // ZoomHistory.lastIntegerZoom should be 0.
+ map->setZoom(0.5);
+ test::checkImage("test/fixtures/zoom_history", frontend.render(*map), 0.0002);
+
+ map->setZoom(1.0);
+ frontend.render(*map);
+
+ map->setZoom(0.5);
+ test::checkImage("test/fixtures/zoom_history", frontend.render(*map), 0.0002);
+}
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/empty-zoomed.json b/test/fixtures/api/empty-zoomed.json
new file mode 100644
index 0000000000..02d8fca99e
--- /dev/null
+++ b/test/fixtures/api/empty-zoomed.json
@@ -0,0 +1,6 @@
+{
+ "version": 8,
+ "zoom": 0.5,
+ "sources": {},
+ "layers": []
+}
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/nocontent/expected.png b/test/fixtures/map/nocontent/expected.png
new file mode 100644
index 0000000000..8fa3f0f5db
--- /dev/null
+++ b/test/fixtures/map/nocontent/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/empty.json b/test/fixtures/map/prefetch/empty.json
new file mode 100644
index 0000000000..61a8fadcdb
--- /dev/null
+++ b/test/fixtures/map/prefetch/empty.json
@@ -0,0 +1,5 @@
+{
+ "version": 8,
+ "sources": {},
+ "layers": []
+}
diff --git a/test/fixtures/map/prefetch/expected.png b/test/fixtures/map/prefetch/expected.png
new file mode 100644
index 0000000000..e1111b37f7
--- /dev/null
+++ b/test/fixtures/map/prefetch/expected.png
Binary files differ
diff --git a/test/fixtures/map/prefetch/style.json b/test/fixtures/map/prefetch/style.json
new file mode 100644
index 0000000000..b4e163888c
--- /dev/null
+++ b/test/fixtures/map/prefetch/style.json
@@ -0,0 +1,24 @@
+{
+ "version": 8,
+ "name": "Test",
+ "sources": {
+ "raster": {
+ "type": "raster",
+ "tiles": [ "{z}" ],
+ "tileSize": 256,
+ "maxzoom": 20,
+ "minzoom": 0
+ }
+ },
+ "layers": [{
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "blue"
+ }
+ }, {
+ "id": "raster",
+ "type": "raster",
+ "source": "raster"
+ }]
+}
diff --git a/test/fixtures/map/prefetch/tile_green.png b/test/fixtures/map/prefetch/tile_green.png
new file mode 100644
index 0000000000..553cd10cd1
--- /dev/null
+++ b/test/fixtures/map/prefetch/tile_green.png
Binary files differ
diff --git a/test/fixtures/map/prefetch/tile_red.png b/test/fixtures/map/prefetch/tile_red.png
new file mode 100644
index 0000000000..5fa561fb92
--- /dev/null
+++ b/test/fixtures/map/prefetch/tile_red.png
Binary files differ
diff --git a/test/fixtures/offline_database/v5.db b/test/fixtures/offline_database/v5.db
new file mode 100644
index 0000000000..9bba351208
--- /dev/null
+++ b/test/fixtures/offline_database/v5.db
Binary files differ
diff --git a/test/fixtures/offline_database/v999.db b/test/fixtures/offline_database/v999.db
new file mode 100644
index 0000000000..97c36cce36
--- /dev/null
+++ b/test/fixtures/offline_database/v999.db
Binary files differ
diff --git a/test/fixtures/recycle_map/default_marker/expected.png b/test/fixtures/recycle_map/default_marker/expected.png
new file mode 100644
index 0000000000..a23b79f8d8
--- /dev/null
+++ b/test/fixtures/recycle_map/default_marker/expected.png
Binary files differ
diff --git a/test/fixtures/recycle_map/flipped_marker/expected.png b/test/fixtures/recycle_map/flipped_marker/expected.png
new file mode 100644
index 0000000000..3c4847f3a7
--- /dev/null
+++ b/test/fixtures/recycle_map/flipped_marker/expected.png
Binary files differ
diff --git a/test/fixtures/shared_context/expected.png b/test/fixtures/shared_context/expected.png
new file mode 100644
index 0000000000..3b3fd0e315
--- /dev/null
+++ b/test/fixtures/shared_context/expected.png
Binary files differ
diff --git a/test/fixtures/style_parser/expressions.info.json b/test/fixtures/style_parser/expressions.info.json
new file mode 100644
index 0000000000..9e1765ecd0
--- /dev/null
+++ b/test/fixtures/style_parser/expressions.info.json
@@ -0,0 +1,12 @@
+{
+ "default": {
+ "log": [
+ [1, "WARNING", "ParseStyle", "Expected color but found number instead."],
+ [1, "WARNING", "ParseStyle", "[2]: Expected number but found string instead."],
+ [1, "WARNING", "ParseStyle", "\"zoom\" expression may only be used as input to a top-level \"step\" or \"interpolate\" expression."],
+ [1, "WARNING", "ParseStyle", "value must be a string"],
+ [1, "WARNING", "ParseStyle", "property expressions not supported"],
+ [1, "WARNING", "ParseStyle", "Type array<number> is not interpolatable."]
+ ]
+ }
+}
diff --git a/test/fixtures/style_parser/expressions.style.json b/test/fixtures/style_parser/expressions.style.json
new file mode 100644
index 0000000000..b9b4aeac7f
--- /dev/null
+++ b/test/fixtures/style_parser/expressions.style.json
@@ -0,0 +1,74 @@
+{
+ "version": 8,
+ "sources": {
+ "source": {
+ "type": "vector",
+ "url": "mapbox://mapbox.mapbox-streets-v5"
+ }
+ },
+ "layers": [
+ {
+ "id": "valid expression",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-color": ["rgba", 10, ["number", ["get", "x"]], 30, 1]
+ }
+ },
+ {
+ "id": "invalid expression type - color",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-color": ["pi"]
+ }
+ },
+ {
+ "id": "invalid expression - fails type checking",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-color": ["rgba", 1, "should be a number", 0, 1]
+ }
+ },
+ {
+ "id": "invalid expression - nested zoom expression",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-opacity": ["+", 0.5, ["interpolate", ["linear"], ["zoom"], 0, 0, 1, 1]]
+ }
+ },
+ {
+ "id": "invalid expression - not allowed in visibility",
+ "type": "fill",
+ "source": "source",
+ "source-layer": "layer",
+ "layout": {
+ "visibility": ["literal", true]
+ }
+ },
+ {
+ "id": "invalid expression - not a DDS property",
+ "type": "fill-extrusion",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "fill-extrusion-opacity": ["get", "opacity"]
+ }
+ },
+ {
+ "id": "invalid expression - line-dasharray must use step interpolation",
+ "type": "line",
+ "source": "source",
+ "source-layer": "layer",
+ "paint": {
+ "line-dasharray": ["interpolate", ["linear"], ["zoom"], 0, ["literal", [1, 2]], 1, ["literal", [3, 4]]]
+ }
+ }
+ ]
+}
diff --git a/test/fixtures/style_parser/geojson-missing-properties.info.json b/test/fixtures/style_parser/geojson-missing-properties.info.json
new file mode 100644
index 0000000000..9c25a2f488
--- /dev/null
+++ b/test/fixtures/style_parser/geojson-missing-properties.info.json
@@ -0,0 +1,6 @@
+{
+ "default": {
+ "log": [
+ ]
+ }
+}
diff --git a/test/fixtures/style_parser/geojson-missing-properties.style.json b/test/fixtures/style_parser/geojson-missing-properties.style.json
new file mode 100644
index 0000000000..fc4fe97c78
--- /dev/null
+++ b/test/fixtures/style_parser/geojson-missing-properties.style.json
@@ -0,0 +1,9 @@
+{
+ "version": 8,
+ "sources": {
+ "mapbox": {
+ "type": "geojson",
+ "data": { "type": "Feature", "geometry": { "type": "Point", "coordinates": [100.0, 0.0] } }
+ }
+ }
+}
diff --git a/test/fixtures/zoom_history/expected.png b/test/fixtures/zoom_history/expected.png
new file mode 100644
index 0000000000..100dc508a4
--- /dev/null
+++ b/test/fixtures/zoom_history/expected.png
Binary files differ
diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp
index ee9ea54414..8393fd130d 100644
--- a/test/gl/bucket.test.cpp
+++ b/test/gl/bucket.test.cpp
@@ -1,5 +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>
@@ -8,42 +10,141 @@
#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>
+namespace mbgl {
+
+template <class Attributes>
+bool operator==(const Segment<Attributes>& lhs, const Segment<Attributes>& rhs) {
+ return std::tie(lhs.vertexOffset, lhs.indexOffset, lhs.vertexLength, lhs.indexLength) ==
+ std::tie(rhs.vertexOffset, rhs.indexOffset, rhs.vertexLength, rhs.indexLength);
+}
+
+namespace gl {
+namespace detail {
+
+template <class A1, class A2>
+bool operator==(const Vertex<A1, A2>& lhs, const Vertex<A1, A2>& rhs) {
+ return std::tie(lhs.a1, lhs.a2) == std::tie(rhs.a1, rhs.a2);
+}
+
+} // namespace detail
+} // namespace gl
+} // namespace mbgl
+
using namespace mbgl;
+namespace {
+
+PropertyMap properties;
+
+} // namespace
+
TEST(Buckets, CircleBucket) {
- CircleBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {} };
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
+ gl::Context context;
+ CircleBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {} };
ASSERT_FALSE(bucket.hasData());
+ ASSERT_FALSE(bucket.needsUpload());
+
+ GeometryCollection point { { { 0, 0 } } };
+ bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point);
+ ASSERT_TRUE(bucket.hasData());
+ ASSERT_TRUE(bucket.needsUpload());
+
+ bucket.upload(context);
+ ASSERT_TRUE(bucket.hasData());
+ ASSERT_FALSE(bucket.needsUpload());
}
TEST(Buckets, FillBucket) {
- FillBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {} };
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
+ gl::Context context;
+ FillBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {} };
ASSERT_FALSE(bucket.hasData());
+ ASSERT_FALSE(bucket.needsUpload());
+
+ GeometryCollection polygon { { { 0, 0 }, { 0, 1 }, { 1, 1 } } };
+ bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Polygon, polygon, properties }, polygon);
+ ASSERT_TRUE(bucket.hasData());
+ ASSERT_TRUE(bucket.needsUpload());
+
+ bucket.upload(context);
+ ASSERT_FALSE(bucket.needsUpload());
}
TEST(Buckets, LineBucket) {
- LineBucket bucket { { {0, 0, 0}, MapMode::Still, 1.0 }, {}, {} };
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
+ gl::Context context;
+ LineBucket bucket { { {0, 0, 0}, MapMode::Static, 1.0 }, {}, {} };
ASSERT_FALSE(bucket.hasData());
+ ASSERT_FALSE(bucket.needsUpload());
+
+ // Ignore invalid feature type.
+ GeometryCollection point { { { 0, 0 } } };
+ bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point);
+ ASSERT_FALSE(bucket.hasData());
+
+ GeometryCollection line { { { 0, 0 }, { 1, 1 } } };
+ bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::LineString, line, properties }, line);
+ ASSERT_TRUE(bucket.hasData());
+ ASSERT_TRUE(bucket.needsUpload());
+
+ bucket.upload(context);
+ ASSERT_FALSE(bucket.needsUpload());
}
TEST(Buckets, SymbolBucket) {
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
style::SymbolLayoutProperties::PossiblyEvaluated layout;
bool sdfIcons = false;
bool iconsNeedLinear = false;
+ bool sortFeaturesByY = false;
+ std::vector<SymbolInstance> symbolInstances;
- SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear };
+ gl::Context context;
+ SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(symbolInstances) };
ASSERT_FALSE(bucket.hasIconData());
ASSERT_FALSE(bucket.hasTextData());
ASSERT_FALSE(bucket.hasCollisionBoxData());
+ ASSERT_FALSE(bucket.hasData());
+ ASSERT_FALSE(bucket.needsUpload());
+
+ // SymbolBucket::addFeature() is a no-op.
+ GeometryCollection point { { { 0, 0 } } };
+ bucket.addFeature(StubGeometryTileFeature { {}, FeatureType::Point, point, properties }, point);
+ ASSERT_FALSE(bucket.hasData());
+ ASSERT_FALSE(bucket.needsUpload());
+
+ bucket.text.segments.emplace_back(0, 0);
+ ASSERT_TRUE(bucket.hasTextData());
+ ASSERT_TRUE(bucket.hasData());
+ ASSERT_TRUE(bucket.needsUpload());
+
+ bucket.upload(context);
+ ASSERT_FALSE(bucket.needsUpload());
}
TEST(Buckets, RasterBucket) {
+ HeadlessBackend backend({ 512, 256 });
+ BackendScope scope { backend };
+
gl::Context context;
- UnassociatedImage rgba({ 1, 1 });
+ PremultipliedImage rgba({ 1, 1 });
+ // RasterBucket::hasData() is always true.
RasterBucket bucket = { std::move(rgba) };
+ ASSERT_TRUE(bucket.hasData());
ASSERT_TRUE(bucket.needsUpload());
bucket.upload(context);
@@ -52,3 +153,142 @@ TEST(Buckets, RasterBucket) {
bucket.clear();
ASSERT_TRUE(bucket.needsUpload());
}
+
+TEST(Buckets, RasterBucketMaskEmpty) {
+ RasterBucket bucket{ nullptr };
+ bucket.setMask({});
+ EXPECT_EQ((std::vector<RasterLayoutVertex>{}), bucket.vertices.vector());
+ EXPECT_EQ((std::vector<uint16_t>{}), bucket.indices.vector());
+ SegmentVector<RasterAttributes> expectedSegments;
+ expectedSegments.emplace_back(0, 0, 0, 0);
+ EXPECT_EQ(expectedSegments, bucket.segments);
+}
+
+TEST(Buckets, RasterBucketMaskNoChildren) {
+ RasterBucket bucket{ nullptr };
+ bucket.setMask({ CanonicalTileID{ 0, 0, 0 } });
+
+ // A mask of 0/0/0 doesn't produce buffers since we're instead using the global shared buffers.
+ EXPECT_EQ((std::vector<RasterLayoutVertex>{}), bucket.vertices.vector());
+ EXPECT_EQ((std::vector<uint16_t>{}), bucket.indices.vector());
+ EXPECT_EQ((SegmentVector<RasterAttributes>{}), bucket.segments);
+}
+
+ TEST(Buckets, RasterBucketMaskTwoChildren) {
+ RasterBucket bucket{ nullptr };
+ bucket.setMask(
+ { CanonicalTileID{ 1, 0, 0 }, CanonicalTileID{ 1, 1, 1 } });
+
+ EXPECT_EQ(
+ (std::vector<RasterLayoutVertex>{
+ // 1/0/1
+ RasterProgram::layoutVertex({ 0, 0 }, { 0, 0 }),
+ RasterProgram::layoutVertex({ 4096, 0 }, { 4096, 0 }),
+ RasterProgram::layoutVertex({ 0, 4096 }, { 0, 4096 }),
+ RasterProgram::layoutVertex({ 4096, 4096 }, { 4096, 4096 }),
+
+ // 1/1/1
+ RasterProgram::layoutVertex({ 4096, 4096 }, { 4096, 4096 }),
+ RasterProgram::layoutVertex({ 8192, 4096 }, { 8192, 4096 }),
+ RasterProgram::layoutVertex({ 4096, 8192 }, { 4096, 8192 }),
+ RasterProgram::layoutVertex({ 8192, 8192 }, { 8192, 8192 }),
+ }),
+ bucket.vertices.vector());
+
+ EXPECT_EQ(
+ (std::vector<uint16_t>{
+ // 1/0/1
+ 0, 1, 2,
+ 1, 2, 3,
+
+ // 1/1/1
+ 4, 5, 6,
+ 5, 6, 7,
+ }),
+ bucket.indices.vector());
+
+
+ SegmentVector<RasterAttributes> expectedSegments;
+ expectedSegments.emplace_back(0, 0, 8, 12);
+ EXPECT_EQ(expectedSegments, bucket.segments);
+ }
+
+ TEST(Buckets, RasterBucketMaskComplex) {
+ RasterBucket bucket{ nullptr };
+ bucket.setMask(
+ { CanonicalTileID{ 1, 0, 1 }, CanonicalTileID{ 1, 1, 0 }, CanonicalTileID{ 2, 2, 3 },
+ CanonicalTileID{ 2, 3, 2 }, CanonicalTileID{ 3, 6, 7 }, CanonicalTileID{ 3, 7, 6 } });
+
+ EXPECT_EQ(
+ (std::vector<RasterLayoutVertex>{
+ // 1/0/1
+ RasterProgram::layoutVertex({ 0, 4096 }, { 0, 4096 }),
+ RasterProgram::layoutVertex({ 4096, 4096 }, { 4096, 4096 }),
+ RasterProgram::layoutVertex({ 0, 8192 }, { 0, 8192 }),
+ RasterProgram::layoutVertex({ 4096, 8192 }, { 4096, 8192 }),
+
+ // 1/1/0
+ RasterProgram::layoutVertex({ 4096, 0 }, { 4096, 0 }),
+ RasterProgram::layoutVertex({ 8192, 0 }, { 8192, 0 }),
+ RasterProgram::layoutVertex({ 4096, 4096 }, { 4096, 4096 }),
+ RasterProgram::layoutVertex({ 8192, 4096 }, { 8192, 4096 }),
+
+ // 2/2/3
+ RasterProgram::layoutVertex({ 4096, 6144 }, { 4096, 6144 }),
+ RasterProgram::layoutVertex({ 6144, 6144 }, { 6144, 6144 }),
+ RasterProgram::layoutVertex({ 4096, 8192 }, { 4096, 8192 }),
+ RasterProgram::layoutVertex({ 6144, 8192 }, { 6144, 8192 }),
+
+ // 2/3/2
+ RasterProgram::layoutVertex({ 6144, 4096 }, { 6144, 4096 }),
+ RasterProgram::layoutVertex({ 8192, 4096 }, { 8192, 4096 }),
+ RasterProgram::layoutVertex({ 6144, 6144 }, { 6144, 6144 }),
+ RasterProgram::layoutVertex({ 8192, 6144 }, { 8192, 6144 }),
+
+ // 3/6/7
+ RasterProgram::layoutVertex({ 6144, 7168 }, { 6144, 7168 }),
+ RasterProgram::layoutVertex({ 7168, 7168 }, { 7168, 7168 }),
+ RasterProgram::layoutVertex({ 6144, 8192 }, { 6144, 8192 }),
+ RasterProgram::layoutVertex({ 7168, 8192 }, { 7168, 8192 }),
+
+ // 3/7/6
+ RasterProgram::layoutVertex({ 7168, 6144 }, { 7168, 6144 }),
+ RasterProgram::layoutVertex({ 8192, 6144 }, { 8192, 6144 }),
+ RasterProgram::layoutVertex({ 7168, 7168 }, { 7168, 7168 }),
+ RasterProgram::layoutVertex({ 8192, 7168 }, { 8192, 7168 }),
+ }),
+ bucket.vertices.vector());
+
+ EXPECT_EQ(
+ (std::vector<uint16_t>{
+ // 1/0/1
+ 0, 1, 2,
+ 1, 2, 3,
+
+ // 1/1/0
+ 4, 5, 6,
+ 5, 6, 7,
+
+ // 2/2/3
+ 8, 9, 10,
+ 9, 10, 11,
+
+ // 2/3/2
+ 12, 13, 14,
+ 13, 14, 15,
+
+ // 3/6/7
+ 16, 17, 18,
+ 17, 18, 19,
+
+ // 3/7/6
+ 20, 21, 22,
+ 21, 22, 23,
+ }),
+ bucket.indices.vector());
+
+
+ SegmentVector<RasterAttributes> expectedSegments;
+ expectedSegments.emplace_back(0, 0, 24, 36);
+ EXPECT_EQ(expectedSegments, bucket.segments);
+ }
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/gl/object.test.cpp b/test/gl/object.test.cpp
index b5a055f4ca..8046927c54 100644
--- a/test/gl/object.test.cpp
+++ b/test/gl/object.test.cpp
@@ -1,9 +1,7 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
-
#include <mbgl/gl/context.hpp>
#include <memory>
@@ -47,9 +45,8 @@ TEST(GLObject, Value) {
}
TEST(GLObject, Store) {
- HeadlessBackend backend { test::sharedDisplay() };
+ HeadlessBackend backend { { 256, 256 } };
BackendScope scope { backend };
- OffscreenView view(backend.getContext());
gl::Context context;
EXPECT_TRUE(context.empty());
diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp
index bed46f2c3e..9b34ea89b0 100644
--- a/test/map/map.test.cpp
+++ b/test/map/map.test.cpp
@@ -4,10 +4,8 @@
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -25,107 +23,142 @@ using namespace mbgl;
using namespace mbgl::style;
using namespace std::literals::string_literals;
-class BackendTest : public HeadlessBackend {
+class StubMapObserver : public MapObserver {
public:
- BackendTest() : HeadlessBackend(test::sharedDisplay()) {}
-
- void invalidate() {
- if (invalidateCallback) {
- invalidateCallback();
- }
- }
-
- std::function<void()> invalidateCallback;
-
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;
};
-struct MapTest {
+template <class FileSource = StubFileSource>
+class MapTest {
+public:
util::RunLoop runLoop;
- BackendTest backend;
- BackendScope scope { backend };
- OffscreenView view { backend.getContext() };
- StubFileSource fileSource;
+ FileSource fileSource;
ThreadPool threadPool { 4 };
+ StubMapObserver observer;
+ HeadlessFrontend frontend;
+ Map map;
+
+ 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::Static,
+ typename std::enable_if<std::is_same<T, DefaultFileSource>::value>::type* = 0)
+ : fileSource { cachePath, assetRoot }
+ , frontend(pixelRatio, fileSource, threadPool)
+ , map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, mode) {
+ }
};
TEST(Map, LatLngBehavior) {
- MapTest test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
+ MapTest<> test;
- map.setLatLngZoom({ 1, 1 }, 0);
- auto latLng1 = map.getLatLng();
+ test.map.setLatLngZoom({ 1, 1 }, 0);
+ auto latLng1 = test.map.getLatLng();
- map.setLatLng({ 1, 1 });
- auto latLng2 = map.getLatLng();
+ test.map.setLatLng({ 1, 1 });
+ auto latLng2 = test.map.getLatLng();
ASSERT_DOUBLE_EQ(latLng1.latitude(), latLng2.latitude());
ASSERT_DOUBLE_EQ(latLng1.longitude(), latLng2.longitude());
}
TEST(Map, LatLngBoundsToCamera) {
- MapTest test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
+ MapTest<> test;
+
+ test.map.setLatLngZoom({ 40.712730, -74.005953 }, 16.0);
+
+ LatLngBounds bounds = LatLngBounds::hull({15.68169,73.499857}, {53.560711, 134.77281});
+
+ CameraOptions virtualCamera = test.map.cameraForLatLngBounds(bounds, {});
+ ASSERT_TRUE(bounds.contains(*virtualCamera.center));
+ EXPECT_NEAR(*virtualCamera.zoom, 1.55467, 1e-5);
+}
+
+TEST(Map, LatLngBoundsToCameraWithAngle) {
+ MapTest<> test;
- map.setLatLngZoom({ 40.712730, -74.005953 }, 16.0);
+ test.map.setLatLngZoom({ 40.712730, -74.005953 }, 16.0);
LatLngBounds bounds = LatLngBounds::hull({15.68169,73.499857}, {53.560711, 134.77281});
- CameraOptions virtualCamera = map.cameraForLatLngBounds(bounds, {});
+ CameraOptions virtualCamera = test.map.cameraForLatLngBounds(bounds, {}, 35);
ASSERT_TRUE(bounds.contains(*virtualCamera.center));
+ EXPECT_NEAR(*virtualCamera.zoom, 1.21385, 1e-5);
+ EXPECT_DOUBLE_EQ(virtualCamera.angle.value_or(0), -35 * util::DEG2RAD);
+}
+
+TEST(Map, LatLngsToCamera) {
+ MapTest<> test;
+
+ std::vector<LatLng> latLngs{{ 40.712730, 74.005953 }, {15.68169,73.499857}, {30.82678, 83.4082}};
+
+ CameraOptions virtualCamera = test.map.cameraForLatLngs(latLngs, {}, 23);
+ EXPECT_DOUBLE_EQ(virtualCamera.angle.value_or(0), -23 * util::DEG2RAD);
+ EXPECT_NEAR(virtualCamera.zoom.value_or(0), 2.75434, 1e-5);
+ EXPECT_NEAR(virtualCamera.center->latitude(), 28.49288, 1e-5);
+ EXPECT_NEAR(virtualCamera.center->longitude(), 74.97437, 1e-5);
}
TEST(Map, CameraToLatLngBounds) {
- MapTest test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
+ MapTest<> test;
- map.setLatLngZoom({ 45, 90 }, 16);
+ test.map.setLatLngZoom({ 45, 90 }, 16);
LatLngBounds bounds = LatLngBounds::hull(
- map.latLngForPixel({}),
- map.latLngForPixel({ double(map.getSize().width), double(map.getSize().height) }));
+ test.map.latLngForPixel({}),
+ test.map.latLngForPixel({ double(test.map.getSize().width), double(test.map.getSize().height) }));
- CameraOptions camera = map.getCameraOptions({});
+ CameraOptions camera = test.map.getCameraOptions({});
- ASSERT_EQ(bounds, map.latLngBoundsForCamera(camera));
+ ASSERT_EQ(bounds, test.map.latLngBoundsForCamera(camera));
// Map::cameraForLatLngBounds only sets zoom and center.
- CameraOptions virtualCamera = map.cameraForLatLngBounds(bounds, {});
+ CameraOptions virtualCamera = test.map.cameraForLatLngBounds(bounds, {});
ASSERT_NEAR(*camera.zoom, *virtualCamera.zoom, 1e-7);
ASSERT_NEAR(camera.center->latitude(), virtualCamera.center->latitude(), 1e-7);
ASSERT_NEAR(camera.center->longitude(), virtualCamera.center->longitude(), 1e-7);
}
TEST(Map, Offline) {
- MapTest test;
- DefaultFileSource fileSource(":memory:", ".");
+ MapTest<DefaultFileSource> test {":memory:", "."};
auto expiredItem = [] (const std::string& path) {
Response response;
@@ -135,39 +168,50 @@ TEST(Map, Offline) {
};
const std::string prefix = "http://127.0.0.1:3000/";
- fileSource.put(Resource::style(prefix + "style.json"), expiredItem("style.json"));
- fileSource.put(Resource::source(prefix + "streets.json"), expiredItem("streets.json"));
- fileSource.put(Resource::spriteJSON(prefix + "sprite", 1.0), expiredItem("sprite.json"));
- fileSource.put(Resource::spriteImage(prefix + "sprite", 1.0), expiredItem("sprite.png"));
- fileSource.put(Resource::tile(prefix + "{z}-{x}-{y}.vector.pbf", 1.0, 0, 0, 0, Tileset::Scheme::XYZ), expiredItem("0-0-0.vector.pbf"));
- fileSource.put(Resource::glyphs(prefix + "{fontstack}/{range}.pbf", {{"Helvetica"}}, {0, 255}), expiredItem("glyph.pbf"));
+ test.fileSource.put(Resource::style(prefix + "style.json"), expiredItem("style.json"));
+ test.fileSource.put(Resource::source(prefix + "streets.json"), expiredItem("streets.json"));
+ test.fileSource.put(Resource::spriteJSON(prefix + "sprite", 1.0), expiredItem("sprite.json"));
+ test.fileSource.put(Resource::spriteImage(prefix + "sprite", 1.0), expiredItem("sprite.png"));
+ test.fileSource.put(Resource::tile(prefix + "{z}-{x}-{y}.vector.pbf", 1.0, 0, 0, 0, Tileset::Scheme::XYZ), expiredItem("0-0-0.vector.pbf"));
+ test.fileSource.put(Resource::glyphs(prefix + "{fontstack}/{range}.pbf", {{"Helvetica"}}, {0, 255}), expiredItem("glyph.pbf"));
NetworkStatus::Set(NetworkStatus::Status::Offline);
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadURL(prefix + "style.json");
+ test.map.getStyle().loadURL(prefix + "style.json");
test::checkImage("test/fixtures/map/offline",
- test::render(map, test.view),
+ test.frontend.render(test.map),
0.0015,
0.1);
NetworkStatus::Set(NetworkStatus::Status::Online);
}
-TEST(Map, SetStyleInvalidJSON) {
- MapTest test;
+TEST(Map, SetStyleDefaultCamera) {
+ MapTest<> test;
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ EXPECT_DOUBLE_EQ(test.map.getZoom(), 0.0);
+ EXPECT_DOUBLE_EQ(test.map.getPitch(), 0.0);
+ EXPECT_DOUBLE_EQ(test.map.getBearing(), 0.0);
+ EXPECT_EQ(test.map.getLatLng(), LatLng {});
+
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty-zoomed.json"));
+ EXPECT_DOUBLE_EQ(test.map.getZoom(), 0.0);
+ test.map.jumpTo(test.map.getStyle().getDefaultCamera());
+ EXPECT_DOUBLE_EQ(test.map.getZoom(), 0.5);
+}
+
+TEST(Map, SetStyleInvalidJSON) {
Log::setObserver(std::make_unique<FixtureLogObserver>());
bool fail = false;
- test.backend.didFailLoadingMapCallback = [&]() {
- fail = true;
- };
{
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool,
- MapMode::Still);
- map.getStyle().loadJSON("invalid");
+ MapTest<> test;
+ test.observer.didFailLoadingMapCallback = [&]() {
+ fail = true;
+ };
+ test.map.getStyle().loadJSON("invalid");
}
EXPECT_TRUE(fail);
@@ -181,7 +225,7 @@ TEST(Map, SetStyleInvalidJSON) {
}
TEST(Map, SetStyleInvalidURL) {
- MapTest test;
+ MapTest<> test;
test.fileSource.styleResponse = [] (const Resource&) {
Response response;
@@ -191,40 +235,36 @@ TEST(Map, SetStyleInvalidURL) {
return response;
};
- test.backend.didFailLoadingMapCallback = [&]() {
+ test.observer.didFailLoadingMapCallback = [&]() {
test.runLoop.stop();
};
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadURL("mapbox://bar");
+ test.map.getStyle().loadURL("mapbox://bar");
test.runLoop.run();
}
TEST(Map, DoubleStyleLoad) {
- MapTest test;
+ MapTest<> test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadJSON("");
- map.getStyle().loadJSON("");
+ test.map.getStyle().loadJSON("");
+ test.map.getStyle().loadJSON("");
}
TEST(Map, StyleFresh) {
// The map should not revalidate fresh styles.
- MapTest test;
- FakeFileSource fileSource;
+ MapTest<FakeFileSource> test;
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadURL("mapbox://styles/test");
- EXPECT_EQ(1u, fileSource.requests.size());
+ test.map.getStyle().loadURL("mapbox://styles/test");
+ EXPECT_EQ(1u, test.fileSource.requests.size());
Response response;
response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json"));
response.expires = Timestamp::max();
- fileSource.respond(Resource::Style, response);
- EXPECT_EQ(0u, fileSource.requests.size());
+ test.fileSource.respond(Resource::Style, response);
+ EXPECT_EQ(0u, test.fileSource.requests.size());
}
TEST(Map, StyleExpired) {
@@ -232,26 +272,24 @@ TEST(Map, StyleExpired) {
using namespace std::chrono_literals;
- MapTest test;
- FakeFileSource fileSource;
+ MapTest<FakeFileSource> test;
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadURL("mapbox://styles/test");
- EXPECT_EQ(1u, fileSource.requests.size());
+ test.map.getStyle().loadURL("mapbox://styles/test");
+ EXPECT_EQ(1u, test.fileSource.requests.size());
Response response;
response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json"));
response.expires = util::now() - 1h;
- fileSource.respond(Resource::Style, response);
- EXPECT_EQ(1u, fileSource.requests.size());
+ test.fileSource.respond(Resource::Style, response);
+ EXPECT_EQ(1u, test.fileSource.requests.size());
- map.getStyle().addLayer(std::make_unique<style::BackgroundLayer>("bg"));
- EXPECT_EQ(1u, fileSource.requests.size());
+ test.map.getStyle().addLayer(std::make_unique<style::BackgroundLayer>("bg"));
+ EXPECT_EQ(1u, test.fileSource.requests.size());
- fileSource.respond(Resource::Style, response);
- EXPECT_EQ(0u, fileSource.requests.size());
- EXPECT_NE(nullptr, map.getStyle().getLayer("bg"));
+ test.fileSource.respond(Resource::Style, response);
+ EXPECT_EQ(0u, test.fileSource.requests.size());
+ EXPECT_NE(nullptr, test.map.getStyle().getLayer("bg"));
}
TEST(Map, StyleExpiredWithAnnotations) {
@@ -259,100 +297,111 @@ TEST(Map, StyleExpiredWithAnnotations) {
using namespace std::chrono_literals;
- MapTest test;
- FakeFileSource fileSource;
+ MapTest<FakeFileSource> test;
+
+ test.map.getStyle().loadURL("mapbox://styles/test");
+ EXPECT_EQ(1u, test.fileSource.requests.size());
+
+ Response response;
+ response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json"));
+ response.expires = util::now() - 1h;
+
+ test.fileSource.respond(Resource::Style, response);
+ EXPECT_EQ(1u, test.fileSource.requests.size());
+
+ test.map.addAnnotation(LineAnnotation { LineString<double> {{ { 0, 0 }, { 10, 10 } }} });
+ EXPECT_EQ(1u, test.fileSource.requests.size());
+
+ test.fileSource.respond(Resource::Style, response);
+ EXPECT_EQ(1u, test.fileSource.requests.size());
+}
+
+TEST(Map, StyleExpiredWithRender) {
+ // Rendering should not prevent revalidation of an expired style.
+
+ using namespace std::chrono_literals;
+
+ MapTest<FakeFileSource> test;
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadURL("mapbox://styles/test");
- EXPECT_EQ(1u, fileSource.requests.size());
+ test.map.getStyle().loadURL("mapbox://styles/test");
+ EXPECT_EQ(1u, test.fileSource.requests.size());
Response response;
response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/empty.json"));
response.expires = util::now() - 1h;
- fileSource.respond(Resource::Style, response);
- EXPECT_EQ(1u, fileSource.requests.size());
+ test.fileSource.respond(Resource::Style, response);
+ EXPECT_EQ(1u, test.fileSource.requests.size());
- map.addAnnotation(LineAnnotation { LineString<double> {{ { 0, 0 }, { 10, 10 } }} });
- EXPECT_EQ(1u, fileSource.requests.size());
+ test.frontend.render(test.map);
+ EXPECT_EQ(1u, test.fileSource.requests.size());
- fileSource.respond(Resource::Style, response);
- EXPECT_EQ(1u, fileSource.requests.size());
+ test.fileSource.respond(Resource::Style, response);
+ EXPECT_EQ(1u, test.fileSource.requests.size());
}
TEST(Map, StyleEarlyMutation) {
// An early mutation should not prevent the initial style load.
- MapTest test;
- FakeFileSource fileSource;
+ MapTest<FakeFileSource> test;
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadURL("mapbox://styles/test");
- map.getStyle().addLayer(std::make_unique<style::BackgroundLayer>("bg"));
+ test.map.getStyle().loadURL("mapbox://styles/test");
+ test.map.getStyle().addLayer(std::make_unique<style::BackgroundLayer>("bg"));
Response response;
response.data = std::make_shared<std::string>(util::read_file("test/fixtures/api/water.json"));
- fileSource.respond(Resource::Style, response);
+ test.fileSource.respond(Resource::Style, response);
- EXPECT_EQ(0u, fileSource.requests.size());
- EXPECT_NE(nullptr, map.getStyle().getLayer("water"));
+ EXPECT_EQ(0u, test.fileSource.requests.size());
+ EXPECT_NE(nullptr, test.map.getStyle().getLayer("water"));
}
TEST(Map, MapLoadingSignal) {
- MapTest test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
+ MapTest<> test;
bool emitted = false;
- test.backend.onWillStartLoadingMapCallback = [&]() {
+ test.observer.onWillStartLoadingMapCallback = [&]() {
emitted = true;
};
- map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
EXPECT_TRUE(emitted);
}
TEST(Map, MapLoadedSignal) {
- MapTest test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Continuous);
+ MapTest<> test { 1, MapMode::Continuous };
- test.backend.onDidFinishLoadingMapCallback = [&]() {
+ test.observer.onDidFinishLoadingMapCallback = [&]() {
test.runLoop.stop();
};
- test.backend.invalidateCallback = [&]() {
- map.render(test.view);
- };
-
- map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
test.runLoop.run();
}
TEST(Map, StyleLoadedSignal) {
- MapTest test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
+ MapTest<> test;
// The map should emit a signal on style loaded
bool emitted = false;
- test.backend.didFinishLoadingStyleCallback = [&]() {
+ test.observer.didFinishLoadingStyleCallback = [&]() {
emitted = true;
};
- map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
EXPECT_TRUE(emitted);
// But not when the style couldn't be parsed
emitted = false;
- map.getStyle().loadJSON("invalid");
+ test.map.getStyle().loadJSON("invalid");
EXPECT_FALSE(emitted);
}
// Test for https://github.com/mapbox/mapbox-gl-native/issues/7902
TEST(Map, TEST_REQUIRES_SERVER(StyleNetworkErrorRetry)) {
- MapTest test;
- OnlineFileSource fileSource;
+ MapTest<OnlineFileSource> test;
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-500");
+ test.map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-500");
- test.backend.didFinishLoadingStyleCallback = [&]() {
+ test.observer.didFinishLoadingStyleCallback = [&]() {
test.runLoop.stop();
};
@@ -360,21 +409,19 @@ TEST(Map, TEST_REQUIRES_SERVER(StyleNetworkErrorRetry)) {
}
TEST(Map, TEST_REQUIRES_SERVER(StyleNotFound)) {
- MapTest test;
- OnlineFileSource fileSource;
+ MapTest<OnlineFileSource> test;
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-404");
+ test.map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-404");
using namespace std::chrono_literals;
util::Timer timer;
// Not found errors should not trigger a retry like other errors.
- test.backend.didFinishLoadingStyleCallback = [&]() {
+ test.observer.didFinishLoadingStyleCallback = [&]() {
FAIL() << "Should not retry on not found!";
};
- test.backend.didFailLoadingMapCallback = [&]() {
+ test.observer.didFailLoadingMapCallback = [&]() {
timer.start(Milliseconds(1100), 0s, [&] {
test.runLoop.stop();
});
@@ -383,52 +430,48 @@ TEST(Map, TEST_REQUIRES_SERVER(StyleNotFound)) {
test.runLoop.run();
// Should also not retry if the response has cache headers.
- map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-404-cache");
+ test.map.getStyle().loadURL("http://127.0.0.1:3000/style-fail-once-404-cache");
test.runLoop.run();
}
TEST(Map, AddLayer) {
- MapTest test;
+ MapTest<> test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
auto layer = std::make_unique<BackgroundLayer>("background");
layer->setBackgroundColor({ { 1, 0, 0, 1 } });
- map.getStyle().addLayer(std::move(layer));
+ test.map.getStyle().addLayer(std::move(layer));
- test::checkImage("test/fixtures/map/add_layer", test::render(map, test.view));
+ test::checkImage("test/fixtures/map/add_layer", test.frontend.render(test.map));
}
TEST(Map, WithoutVAOExtension) {
- MapTest test;
+ MapTest<DefaultFileSource> test { ":memory:", "test/fixtures/api/assets" };
- test.backend.getContext().disableVAOExtension = true;
+ BackendScope scope { *test.frontend.getBackend() };
+ test.frontend.getBackend()->getContext().disableVAOExtension = true;
- DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
- Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
-
- test::checkImage("test/fixtures/map/no_vao", test::render(map, test.view), 0.002);
+ test::checkImage("test/fixtures/map/no_vao", test.frontend.render(test.map), 0.002);
}
TEST(Map, RemoveLayer) {
- MapTest test;
+ MapTest<> test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
+ test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
auto layer = std::make_unique<BackgroundLayer>("background");
layer->setBackgroundColor({{ 1, 0, 0, 1 }});
- map.getStyle().addLayer(std::move(layer));
- map.getStyle().removeLayer("background");
+ test.map.getStyle().addLayer(std::move(layer));
+ test.map.getStyle().removeLayer("background");
- test::checkImage("test/fixtures/map/remove_layer", test::render(map, test.view));
+ test::checkImage("test/fixtures/map/remove_layer", test.frontend.render(test.map));
}
TEST(Map, DisabledSources) {
- MapTest test;
+ MapTest<> test;
// Always load the same image tile for raster layers.
test.fileSource.response = [] (const Resource& res) -> optional<Response> {
@@ -441,15 +484,14 @@ TEST(Map, DisabledSources) {
return {};
};
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- map.setZoom(1);
+ test.map.setZoom(1);
// This stylesheet has two raster layers, one that starts at zoom 1, the other at zoom 0.
// We first render a map at zoom level 1, which should show both layers (both are "visible" due
// to an opacity of 0.5). Then, we are zooming back out to a zoom level of 0.5 and rerender.
// The "raster1" layer should not be visible anymore since it has minzoom 1, while "raster2"
// should still be there. Both layers have a distinct color through "raster-hue-rotate".
- map.getStyle().loadJSON(R"STYLE(
+ test.map.getStyle().loadJSON(R"STYLE(
{
"version": 8,
"name": "Test",
@@ -483,16 +525,15 @@ TEST(Map, DisabledSources) {
}
)STYLE");
- test::checkImage("test/fixtures/map/disabled_layers/first", test::render(map, test.view));
- map.setZoom(0.5);
- test::checkImage("test/fixtures/map/disabled_layers/second", test::render(map, test.view));
+ test::checkImage("test/fixtures/map/disabled_layers/first", test.frontend.render(test.map));
+ test.map.setZoom(0.5);
+ test::checkImage("test/fixtures/map/disabled_layers/second", test.frontend.render(test.map));
}
TEST(Map, DontLoadUnneededTiles) {
- MapTest test;
+ MapTest<> test;
- Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
- map.getStyle().loadJSON(R"STYLE({
+ test.map.getStyle().loadJSON(R"STYLE({
"sources": {
"a": { "type": "vector", "tiles": [ "a/{z}/{x}/{y}" ] }
},
@@ -530,65 +571,84 @@ TEST(Map, DontLoadUnneededTiles) {
// Note: using z += 0.1 in the loop doesn't produce accurate floating point numbers.
const double z = double(zoom) / 10;
tiles.clear();
- map.setZoom(z);
- test::render(map, test.view);
+ test.map.setZoom(z);
+ test.frontend.render(test.map);
EXPECT_EQ(referenceTiles[z], tiles) << "zoom level " << z;
}
}
-
-class MockBackend : public HeadlessBackend {
-public:
- MockBackend(std::shared_ptr<HeadlessDisplay> display_)
- : HeadlessBackend(display_) {
- }
-
- std::function<void()> callback;
- void invalidate() override {
- if (callback) {
- callback();
- }
- }
-};
-
TEST(Map, TEST_DISABLED_ON_CI(ContinuousRendering)) {
util::RunLoop runLoop;
- MockBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view { backend.getContext() };
ThreadPool threadPool { 4 };
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets");
- Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Continuous);
+ float pixelRatio { 1 };
using namespace std::chrono_literals;
util::Timer emergencyShutoff;
emergencyShutoff.start(10s, 0s, [&] {
- util::RunLoop::Get()->stop();
+ runLoop.stop();
FAIL() << "Did not stop rendering";
});
util::Timer timer;
- util::AsyncTask render{[&] {
- if (map.isFullyLoaded()) {
- // Abort the test after 1 second after the map loading fully. Note that a "fully loaded
- // map" doesn't mean that we won't render anymore: we could still render fade in/fade
- // out or other animations.
- // If we are continuing to render indefinitely, the emergency shutoff above will trigger
- // and the test will fail since the regular time will be constantly reset.
- timer.start(1s, 0s, [&] {
- util::RunLoop::Get()->stop();
- });
- }
- BackendScope scope2(backend);
- map.render(view);
- }};
+ HeadlessFrontend frontend(pixelRatio, fileSource, threadPool);
- backend.callback = [&] {
- render.send();
+ StubMapObserver observer;
+ observer.didFinishRenderingFrame = [&] (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.
+ timer.start(1s, 0s, [&] {
+ runLoop.stop();
+ });
};
+ Map map(frontend, observer, frontend.getSize(), pixelRatio, fileSource, threadPool, MapMode::Continuous);
map.getStyle().loadJSON(util::read_file("test/fixtures/api/water.json"));
- util::RunLoop::Get()->run();
+
+ runLoop.run();
+}
+
+TEST(Map, NoContentTiles) {
+ MapTest<DefaultFileSource> test {":memory:", "."};
+
+ using namespace std::chrono_literals;
+
+ // Insert a 204 No Content response for the 0/0/0 tile
+ Response response;
+ response.noContent = true;
+ response.expires = util::now() + 1h;
+ test.fileSource.put(Resource::tile("http://example.com/{z}-{x}-{y}.vector.pbf", 1.0, 0, 0, 0,
+ Tileset::Scheme::XYZ),
+ response);
+
+ test.map.getStyle().loadJSON(R"STYLE({
+ "version": 8,
+ "name": "Water",
+ "sources": {
+ "mapbox": {
+ "type": "vector",
+ "tiles": ["http://example.com/{z}-{x}-{y}.vector.pbf"]
+ }
+ },
+ "layers": [{
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "red"
+ }
+ }, {
+ "id": "water",
+ "type": "fill",
+ "source": "mapbox",
+ "source-layer": "water"
+ }]
+ })STYLE");
+
+ test::checkImage("test/fixtures/map/nocontent",
+ test.frontend.render(test.map),
+ 0.0015,
+ 0.1);
}
diff --git a/test/map/prefetch.test.cpp b/test/map/prefetch.test.cpp
new file mode 100644
index 0000000000..4c82b2c965
--- /dev/null
+++ b/test/map/prefetch.test.cpp
@@ -0,0 +1,89 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_file_source.hpp>
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/style/style.hpp>
+#include <mbgl/util/default_thread_pool.hpp>
+#include <mbgl/util/image.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/run_loop.hpp>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+using namespace mbgl;
+using namespace mbgl::style;
+using namespace std::literals::string_literals;
+
+TEST(Map, PrefetchTiles) {
+ util::RunLoop runLoop;
+ ThreadPool threadPool(4);
+ StubFileSource fileSource;
+ HeadlessFrontend frontend { { 512, 512 }, 1, fileSource, threadPool };
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Static);
+
+ std::vector<int> tiles;
+
+ fileSource.response = [&] (const Resource& res) -> optional<Response> {
+ Response response;
+
+ 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) };
+ };
+
+ auto checkTilesForZoom = [&](int zoom, const std::vector<int>& expected) {
+ tiles.clear();
+
+ // 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
+
+ // Should always render the ideal tiles (i.e. a green map)
+ test::checkImage("test/fixtures/map/prefetch", frontend.render(map));
+
+ ASSERT_TRUE(std::is_permutation(tiles.begin(), tiles.end(), expected.begin()));
+ ASSERT_FALSE(tiles.empty());
+ };
+
+ // Check defaults, should be 4.
+ ASSERT_EQ(map.getPrefetchZoomDelta(), 4);
+ checkTilesForZoom(12, { 13, 13, 13, 13, 13, 13, 13, 13, 13, 9 });
+
+ // Setting it to 0 disables prefetching.
+ map.setPrefetchZoomDelta(0);
+
+ // No prefetching, raster tiles will use ideal
+ // tiles instead of the actual zoom level, that is
+ // why the zoom levels for non-prefetched tiles are
+ // not the same.
+ checkTilesForZoom(10, { 11, 11, 11, 11, 11, 11, 11, 11, 11 });
+
+ map.setPrefetchZoomDelta(5);
+ checkTilesForZoom(12, { 13, 13, 13, 13, 13, 13, 13, 13, 13, 8 });
+
+ // Should clamp at `minzoom`.
+ map.setPrefetchZoomDelta(20);
+ checkTilesForZoom(10, { 11, 11, 11, 11, 11, 11, 11, 11, 11, 0 });
+
+ // Disabled again.
+ map.setPrefetchZoomDelta(0);
+ checkTilesForZoom(13, { 14, 14, 14, 14, 14, 14, 14, 14, 14 });
+}
diff --git a/test/map/transform.test.cpp b/test/map/transform.test.cpp
index aa49d250b6..11c2c1cc6b 100644
--- a/test/map/transform.test.cpp
+++ b/test/map/transform.test.cpp
@@ -28,6 +28,27 @@ TEST(Transform, InvalidZoom) {
ASSERT_DOUBLE_EQ(0, transform.getLatLng().latitude());
ASSERT_DOUBLE_EQ(0, transform.getLatLng().longitude());
ASSERT_DOUBLE_EQ(1, transform.getZoom());
+
+ transform.setZoom(transform.getState().getMaxZoom() + 0.1);
+ ASSERT_DOUBLE_EQ(transform.getZoom(), transform.getState().getMaxZoom());
+
+ CameraOptions cameraOptions;
+ cameraOptions.center = LatLng { util::LATITUDE_MAX, util::LONGITUDE_MAX };
+ cameraOptions.zoom = transform.getState().getMaxZoom();
+
+ // Executing flyTo with an empty size causes frameZoom to be NaN.
+ transform.flyTo(cameraOptions);
+ transform.updateTransitions(transform.getTransitionStart() + transform.getTransitionDuration());
+ ASSERT_DOUBLE_EQ(transform.getZoom(), transform.getState().getMaxZoom());
+
+ // Executing flyTo with maximum zoom level to the same zoom level causes
+ // frameZoom to be bigger than maximum zoom.
+ transform.resize(Size { 100, 100 });
+ transform.flyTo(cameraOptions);
+ transform.updateTransitions(transform.getTransitionStart() + transform.getTransitionDuration());
+
+ ASSERT_TRUE(transform.getState().valid());
+ ASSERT_DOUBLE_EQ(transform.getState().getMaxZoom(), transform.getZoom());
}
diff --git a/test/programs/binary_program.test.cpp b/test/programs/binary_program.test.cpp
index ce544e7652..a5cf7b6e39 100644
--- a/test/programs/binary_program.test.cpp
+++ b/test/programs/binary_program.test.cpp
@@ -14,12 +14,12 @@ TEST(BinaryProgram, ObtainValues) {
EXPECT_EQ(42u, binaryProgram.format());
EXPECT_EQ("binary code", binaryProgram.code());
EXPECT_EQ("identifier", binaryProgram.identifier());
- EXPECT_EQ(1, binaryProgram.attributeLocation("a_pos"));
- EXPECT_EQ(0, binaryProgram.attributeLocation("u_world"));
- EXPECT_EQ(4, binaryProgram.attributeLocation("a_data"));
+ EXPECT_EQ(1u, binaryProgram.attributeLocation("a_pos"));
+ EXPECT_FALSE(binaryProgram.attributeLocation("u_world"));
+ EXPECT_EQ(4u, binaryProgram.attributeLocation("a_data"));
EXPECT_EQ(1, binaryProgram.uniformLocation("u_world"));
EXPECT_EQ(3, binaryProgram.uniformLocation("u_ratio"));
- EXPECT_EQ(0, binaryProgram.uniformLocation("a_data"));
+ EXPECT_EQ(-1, binaryProgram.uniformLocation("a_data"));
auto serialized = binaryProgram.serialize();
@@ -28,12 +28,12 @@ TEST(BinaryProgram, ObtainValues) {
EXPECT_EQ(42u, binaryProgram2.format());
EXPECT_EQ("binary code", binaryProgram2.code());
EXPECT_EQ("identifier", binaryProgram2.identifier());
- EXPECT_EQ(1, binaryProgram2.attributeLocation("a_pos"));
- EXPECT_EQ(0, binaryProgram2.attributeLocation("u_world"));
- EXPECT_EQ(4, binaryProgram2.attributeLocation("a_data"));
+ EXPECT_EQ(1u, binaryProgram2.attributeLocation("a_pos"));
+ EXPECT_FALSE(binaryProgram2.attributeLocation("u_world"));
+ EXPECT_EQ(4u, binaryProgram2.attributeLocation("a_data"));
EXPECT_EQ(1, binaryProgram2.uniformLocation("u_world"));
EXPECT_EQ(3, binaryProgram2.uniformLocation("u_ratio"));
- EXPECT_EQ(0, binaryProgram2.uniformLocation("a_data"));
+ EXPECT_EQ(-1, binaryProgram2.uniformLocation("a_data"));
EXPECT_THROW(BinaryProgram(""), std::runtime_error);
}
diff --git a/test/programs/symbol_program.test.cpp b/test/programs/symbol_program.test.cpp
index ef1e71c269..62a2e58d7b 100644
--- a/test/programs/symbol_program.test.cpp
+++ b/test/programs/symbol_program.test.cpp
@@ -10,7 +10,6 @@ TEST(SymbolProgram, SymbolSizeBinder) {
EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, true);
EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, true);
EXPECT_EQ(uniformValues.get<uniforms::u_size>().t, 12.0f);
- EXPECT_EQ(uniformValues.get<uniforms::u_layout_size>().t, 12.0f);
binder = SymbolSizeBinder::create(1.0f, style::CameraFunction<float>(style::ExponentialStops<float>({
{0.0f, 8.0f},
@@ -20,7 +19,6 @@ TEST(SymbolProgram, SymbolSizeBinder) {
EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, false);
EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, true);
EXPECT_EQ(uniformValues.get<uniforms::u_size>().t, 9.5f);
- EXPECT_EQ(uniformValues.get<uniforms::u_layout_size>().t, 10.0f);
binder = SymbolSizeBinder::create(0.0f, style::CameraFunction<float>(style::ExponentialStops<float>({
{1.0f, 8.0f},
@@ -30,7 +28,6 @@ TEST(SymbolProgram, SymbolSizeBinder) {
EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, false);
EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, true);
EXPECT_EQ(uniformValues.get<uniforms::u_size>().t, 8.0f);
- EXPECT_EQ(uniformValues.get<uniforms::u_layout_size>().t, 8.0f);
binder = SymbolSizeBinder::create(12.0f, style::CameraFunction<float>(style::ExponentialStops<float>({
{1.0f, 8.0f},
@@ -40,7 +37,6 @@ TEST(SymbolProgram, SymbolSizeBinder) {
EXPECT_EQ(uniformValues.get<uniforms::u_is_size_zoom_constant>().t, false);
EXPECT_EQ(uniformValues.get<uniforms::u_is_size_feature_constant>().t, true);
EXPECT_EQ(uniformValues.get<uniforms::u_size>().t, 18.0f);
- EXPECT_EQ(uniformValues.get<uniforms::u_layout_size>().t, 18.0f);
binder = SymbolSizeBinder::create(0.0f, style::SourceFunction<float>("x", style::ExponentialStops<float>({
{1.0f, 8.0f},
diff --git a/test/renderer/backend_scope.test.cpp b/test/renderer/backend_scope.test.cpp
new file mode 100644
index 0000000000..05b82695b2
--- /dev/null
+++ b/test/renderer/backend_scope.test.cpp
@@ -0,0 +1,112 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+
+#include <functional>
+
+using namespace mbgl;
+
+class StubRendererBackend: public RendererBackend {
+public:
+ void bind() override {
+ }
+
+ mbgl::Size getFramebufferSize() const override {
+ return mbgl::Size{};
+ }
+
+ void activate() override {
+ if (activateFunction) activateFunction();
+ }
+
+ void deactivate() override {
+ if (deactivateFunction) deactivateFunction();
+ }
+
+ void updateAssumedState() override {
+ if (updateAssumedStateFunction) updateAssumedStateFunction();
+ }
+
+ gl::ProcAddress getExtensionFunctionPointer(const char*) override {
+ return {};
+ }
+
+ std::function<void ()> activateFunction;
+ std::function<void ()> deactivateFunction;
+ std::function<void ()> updateAssumedStateFunction;
+};
+
+// A scope should activate on construction
+// and deactivate on descruction (going out
+// of scope)
+TEST(BackendScope, SingleScope) {
+ bool activated;
+ bool deactivated;
+
+ StubRendererBackend backend;
+ backend.activateFunction = [&] { activated = true; };
+ backend.deactivateFunction = [&] { deactivated = true; };
+
+ {
+ BackendScope test { backend };
+ }
+
+ ASSERT_TRUE(activated);
+ ASSERT_TRUE(deactivated);
+}
+
+// With nested scopes, only the outer scope
+// should activate/deactivate
+TEST(BackendScope, NestedScopes) {
+ int activated = 0;
+ int deactivated = 0;
+
+ StubRendererBackend backend;
+ backend.activateFunction = [&] { activated++; };
+ backend.deactivateFunction = [&] { deactivated++; };
+
+ {
+ BackendScope outer { backend };
+ ASSERT_EQ(1, activated);
+ {
+ BackendScope inner { backend };
+ ASSERT_EQ(1, activated);
+ }
+ ASSERT_EQ(0, deactivated);
+ }
+
+ ASSERT_EQ(1, deactivated);
+}
+
+// With chained scopes, where scopes have
+// different backends, the scopes should each
+// activate the scope on entering and de-activating
+// on leaving the scope
+TEST(BackendScope, ChainedScopes) {
+ bool activatedA = false;
+ bool activatedB = false;
+
+ StubRendererBackend backendA;
+ backendA.activateFunction = [&] { activatedA = true; };
+ backendA.deactivateFunction = [&] { activatedA = false; };
+
+ StubRendererBackend backendB;
+ backendB.activateFunction = [&] { activatedB = true; };
+ backendB.deactivateFunction = [&] { activatedB = false; };
+
+ {
+ BackendScope scopeA { backendA };
+ ASSERT_TRUE(activatedA);
+ {
+ BackendScope scopeB { backendB };
+ ASSERT_FALSE(activatedA);
+ ASSERT_TRUE(activatedB);
+ }
+ ASSERT_FALSE(activatedB);
+ ASSERT_TRUE(activatedA);
+ }
+
+ ASSERT_FALSE(activatedA);
+ ASSERT_FALSE(activatedB);
+}
diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp
index 203e05d492..ebe1bcd72f 100644
--- a/test/renderer/image_manager.test.cpp
+++ b/test/renderer/image_manager.test.cpp
@@ -108,11 +108,12 @@ TEST(ImageManager, RemoveReleasesBinPackRect) {
class StubImageRequestor : public ImageRequestor {
public:
- void onImagesAvailable(ImageMap images) final {
- if (imagesAvailable) imagesAvailable(images);
+ void onImagesAvailable(ImageMap images, uint64_t imageCorrelationID_) final {
+ if (imagesAvailable && imageCorrelationID == imageCorrelationID_) imagesAvailable(images);
}
std::function<void (ImageMap)> imagesAvailable;
+ uint64_t imageCorrelationID = 0;
};
TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) {
@@ -124,10 +125,11 @@ TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) {
notified = true;
};
- imageManager.getImages(requestor, {"one"});
+ uint64_t imageCorrelationID = 0;
+ imageManager.getImages(requestor, std::make_pair(std::set<std::string> {"one"}, imageCorrelationID));
ASSERT_FALSE(notified);
- imageManager.onSpriteLoaded();
+ imageManager.setLoaded(true);
ASSERT_TRUE(notified);
}
@@ -140,8 +142,9 @@ TEST(ImageManager, NotifiesRequestorImmediatelyIfDependenciesAreSatisfied) {
notified = true;
};
+ uint64_t imageCorrelationID = 0;
imageManager.addImage(makeMutable<style::Image::Impl>("one", PremultipliedImage({ 16, 16 }), 2));
- imageManager.getImages(requestor, {"one"});
+ imageManager.getImages(requestor, std::make_pair(std::set<std::string> {"one"}, imageCorrelationID));
ASSERT_TRUE(notified);
}
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/fake_file_source.hpp b/test/src/mbgl/test/fake_file_source.hpp
index 3ed3f90a17..baae7f9b7e 100644
--- a/test/src/mbgl/test/fake_file_source.hpp
+++ b/test/src/mbgl/test/fake_file_source.hpp
@@ -42,8 +42,8 @@ public:
}
bool respond(Resource::Kind kind, const Response& response) {
- auto it = std::find_if(requests.begin(), requests.end(), [&] (FakeFileRequest* request) {
- return request->resource.kind == kind;
+ auto it = std::find_if(requests.begin(), requests.end(), [&] (FakeFileRequest* fakeRequest) {
+ return fakeRequest->resource.kind == kind;
});
if (it != requests.end()) {
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_geometry_tile_feature.hpp b/test/src/mbgl/test/stub_geometry_tile_feature.hpp
index 21d198a96b..0164ab133c 100644
--- a/test/src/mbgl/test/stub_geometry_tile_feature.hpp
+++ b/test/src/mbgl/test/stub_geometry_tile_feature.hpp
@@ -9,10 +9,17 @@ public:
: properties(std::move(properties_)) {
}
+ StubGeometryTileFeature(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_, PropertyMap properties_)
+ : properties(std::move(properties_)),
+ id(std::move(id_)),
+ type(type_),
+ geometry(std::move(geometry_)) {
+ }
+
PropertyMap properties;
- optional<FeatureIdentifier> id = {};
+ optional<FeatureIdentifier> id;
FeatureType type = FeatureType::Point;
- GeometryCollection geometry = {};
+ GeometryCollection geometry;
FeatureType getType() const override {
return type;
diff --git a/test/src/mbgl/test/util.cpp b/test/src/mbgl/test/util.cpp
index 7ca2d72504..028a0a9d51 100644
--- a/test/src/mbgl/test/util.cpp
+++ b/test/src/mbgl/test/util.cpp
@@ -1,13 +1,8 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/map/map.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
-#include <mbgl/gl/headless_display.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/run_loop.hpp>
#include <mapbox/pixelmatch.hpp>
@@ -99,24 +94,6 @@ Server::~Server() {
}
}
-std::shared_ptr<HeadlessDisplay> sharedDisplay() {
- static auto display = std::make_shared<HeadlessDisplay>();
- return display;
-}
-
-PremultipliedImage render(Map& map, OffscreenView& view) {
- PremultipliedImage result;
- map.renderStill(view, [&](std::exception_ptr) {
- result = view.readStillImage();
- });
-
- while (!result.valid()) {
- util::RunLoop::Get()->runOnce();
- }
-
- return result;
-}
-
void checkImage(const std::string& base,
const PremultipliedImage& actual,
double imageThreshold,
diff --git a/test/src/mbgl/test/util.hpp b/test/src/mbgl/test/util.hpp
index 8673155fe4..7a8d78897e 100644
--- a/test/src/mbgl/test/util.hpp
+++ b/test/src/mbgl/test/util.hpp
@@ -54,11 +54,6 @@
#include <gtest/gtest.h>
namespace mbgl {
-
-class Map;
-class OffscreenView;
-class HeadlessDisplay;
-
namespace test {
class Server {
@@ -70,10 +65,6 @@ private:
int fd = -1;
};
-std::shared_ptr<HeadlessDisplay> sharedDisplay();
-
-PremultipliedImage render(Map&, OffscreenView&);
-
void checkImage(const std::string& base,
const PremultipliedImage& actual,
double imageThreshold = 0,
diff --git a/test/storage/default_file_source.test.cpp b/test/storage/default_file_source.test.cpp
index 41e305a692..c11d442270 100644
--- a/test/storage/default_file_source.test.cpp
+++ b/test/storage/default_file_source.test.cpp
@@ -22,6 +22,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheResponse)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response 1", *res.data);
EXPECT_TRUE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
response = res;
@@ -34,6 +35,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheResponse)) {
ASSERT_TRUE(res2.data.get());
EXPECT_EQ(*response.data, *res2.data);
EXPECT_EQ(response.expires, res2.expires);
+ EXPECT_EQ(response.mustRevalidate, res2.mustRevalidate);
EXPECT_EQ(response.modified, res2.modified);
EXPECT_EQ(response.etag, res2.etag);
@@ -51,35 +53,53 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateSame)) {
const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
std::unique_ptr<AsyncRequest> req1;
std::unique_ptr<AsyncRequest> req2;
- uint16_t counter = 0;
+ bool gotResponse = false;
// First request causes the response to get cached.
req1 = fs.request(revalidateSame, [&](Response res) {
req1.reset();
EXPECT_EQ(nullptr, res.error);
+ EXPECT_FALSE(res.notModified);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_EQ("snowfall", *res.etag);
- // Second request returns the cached response, then immediately revalidates.
- req2 = fs.request(revalidateSame, [&, res](Response res2) {
- if (counter == 0) {
- ++counter;
+ // The first response is stored in the cache, but it has 'must-revalidate' set. This means
+ // it can't return the cached response right away and we must wait for the revalidation
+ // request to complete. We can distinguish the cached response from the revalided response
+ // because the latter has an expiration date, while the cached response doesn't.
+ req2 = fs.request(revalidateSame, [&](Response res2) {
+ if (!gotResponse) {
+ // Even though we could find the response in the database, we send a revalidation
+ // request and get a 304 response. Since we haven't sent a reply yet, we're forcing
+ // notModified to be false so that implementations can continue to use the
+ // notModified flag to skip parsing new data.
+ gotResponse = true;
+ EXPECT_EQ(nullptr, res2.error);
EXPECT_FALSE(res2.notModified);
+ ASSERT_TRUE(res2.data.get());
+ EXPECT_EQ("Response", *res2.data);
+ EXPECT_TRUE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
+ EXPECT_FALSE(bool(res2.modified));
+ EXPECT_EQ("snowfall", *res2.etag);
} else {
+ // The test server sends a Cache-Control header with a max-age of 1 second. This
+ // means that our OnlineFileSource implementation will request the tile again after
+ // 1 second. This time, our request already had a prior response, so we don't need
+ // to send the data again, and instead can actually forward the notModified flag.
req2.reset();
-
EXPECT_EQ(nullptr, res2.error);
EXPECT_TRUE(res2.notModified);
- ASSERT_FALSE(res2.data.get());
+ EXPECT_FALSE(res2.data.get());
EXPECT_TRUE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
EXPECT_FALSE(bool(res2.modified));
- // We're not sending the ETag in the 304 reply, but it should still be there.
EXPECT_EQ("snowfall", *res2.etag);
-
loop.stop();
}
});
@@ -96,34 +116,53 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateModified)) {
"http://127.0.0.1:3000/revalidate-modified" };
std::unique_ptr<AsyncRequest> req1;
std::unique_ptr<AsyncRequest> req2;
- uint16_t counter = 0;
+ bool gotResponse = false;
// First request causes the response to get cached.
req1 = fs.request(revalidateModified, [&](Response res) {
req1.reset();
EXPECT_EQ(nullptr, res.error);
+ EXPECT_FALSE(res.notModified);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res.modified);
EXPECT_FALSE(res.etag);
- // Second request returns the cached response, then immediately revalidates.
+ // The first response is stored in the cache, but it has 'must-revalidate' set. This means
+ // it can't return the cached response right away and we must wait for the revalidation
+ // request to complete. We can distinguish the cached response from the revalided response
+ // because the latter has an expiration date, while the cached response doesn't.
req2 = fs.request(revalidateModified, [&, res](Response res2) {
- if (counter == 0) {
- ++counter;
+ if (!gotResponse) {
+ // Even though we could find the response in the database, we send a revalidation
+ // request and get a 304 response. Since we haven't sent a reply yet, we're forcing
+ // notModified to be false so that implementations can continue to use the
+ // notModified flag to skip parsing new data.
+ gotResponse = true;
+ EXPECT_EQ(nullptr, res2.error);
EXPECT_FALSE(res2.notModified);
+ ASSERT_TRUE(res2.data.get());
+ EXPECT_EQ("Response", *res2.data);
+ EXPECT_TRUE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
+ EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res2.modified);
+ EXPECT_FALSE(res2.etag);
} else {
+ // The test server sends a Cache-Control header with a max-age of 1 second. This
+ // means that our OnlineFileSource implementation will request the tile again after
+ // 1 second. This time, our request already had a prior response, so we don't need
+ // to send the data again, and instead can actually forward the notModified flag.
req2.reset();
-
EXPECT_EQ(nullptr, res2.error);
EXPECT_TRUE(res2.notModified);
- ASSERT_FALSE(res2.data.get());
+ EXPECT_FALSE(res2.data.get());
EXPECT_TRUE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res2.modified);
EXPECT_FALSE(res2.etag);
-
loop.stop();
}
});
@@ -139,7 +178,6 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) {
const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" };
std::unique_ptr<AsyncRequest> req1;
std::unique_ptr<AsyncRequest> req2;
- uint16_t counter = 0;
// First request causes the response to get cached.
req1 = fs.request(revalidateEtag, [&](Response res) {
@@ -149,27 +187,24 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response 1", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_EQ("response-1", *res.etag);
- // Second request returns the cached response, then immediately revalidates.
+ // Second request does not return the cached response, since it had Cache-Control: must-revalidate set.
req2 = fs.request(revalidateEtag, [&, res](Response res2) {
- if (counter == 0) {
- ++counter;
- EXPECT_FALSE(res2.notModified);
- } else {
- req2.reset();
+ req2.reset();
- EXPECT_EQ(nullptr, res2.error);
- ASSERT_TRUE(res2.data.get());
- EXPECT_NE(res.data, res2.data);
- EXPECT_EQ("Response 2", *res2.data);
- EXPECT_FALSE(bool(res2.expires));
- EXPECT_FALSE(bool(res2.modified));
- EXPECT_EQ("response-2", *res2.etag);
+ EXPECT_EQ(nullptr, res2.error);
+ ASSERT_TRUE(res2.data.get());
+ EXPECT_NE(res.data, res2.data);
+ EXPECT_EQ("Response 2", *res2.data);
+ EXPECT_FALSE(bool(res2.expires));
+ EXPECT_TRUE(res2.mustRevalidate);
+ EXPECT_FALSE(bool(res2.modified));
+ EXPECT_EQ("response-2", *res2.etag);
- loop.stop();
- }
+ loop.stop();
});
});
@@ -203,6 +238,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(HTTPIssue1369)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -215,7 +251,7 @@ TEST(DefaultFileSource, OptionalNonExpired) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
- const Resource optionalResource { Resource::Unknown, "http://127.0.0.1:3000/test", {}, Resource::Optional };
+ const Resource optionalResource { Resource::Unknown, "http://127.0.0.1:3000/test", {}, Resource::LoadingMethod::CacheOnly };
using namespace std::chrono_literals;
@@ -232,6 +268,7 @@ TEST(DefaultFileSource, OptionalNonExpired) {
EXPECT_EQ("Cached value", *res.data);
ASSERT_TRUE(bool(res.expires));
EXPECT_EQ(*response.expires, *res.expires);
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -244,7 +281,7 @@ TEST(DefaultFileSource, OptionalExpired) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
- const Resource optionalResource { Resource::Unknown, "http://127.0.0.1:3000/test", {}, Resource::Optional };
+ const Resource optionalResource { Resource::Unknown, "http://127.0.0.1:3000/test", {}, Resource::LoadingMethod::CacheOnly };
using namespace std::chrono_literals;
@@ -261,6 +298,7 @@ TEST(DefaultFileSource, OptionalExpired) {
EXPECT_EQ("Cached value", *res.data);
ASSERT_TRUE(bool(res.expires));
EXPECT_EQ(*response.expires, *res.expires);
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -289,7 +327,7 @@ TEST(DefaultFileSource, OptionalNotFound) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
- const Resource optionalResource { Resource::Unknown, "http://127.0.0.1:3000/test", {}, Resource::Optional };
+ const Resource optionalResource { Resource::Unknown, "http://127.0.0.1:3000/test", {}, Resource::LoadingMethod::CacheOnly };
using namespace std::chrono_literals;
@@ -301,6 +339,7 @@ TEST(DefaultFileSource, OptionalNotFound) {
EXPECT_EQ("Not found in offline database", res.error->message);
EXPECT_FALSE(res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -309,13 +348,13 @@ TEST(DefaultFileSource, OptionalNotFound) {
loop.run();
}
-// Test that we can make a request with etag data that doesn't first try to load
-// from cache like a regular request
+// Test that a network only request doesn't attempt to load data from the cache.
TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagNotModified)) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
Resource resource { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
+ resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
resource.priorEtag.emplace("snowfall");
using namespace std::chrono_literals;
@@ -334,6 +373,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagNotModified)) {
EXPECT_FALSE(res.data.get());
ASSERT_TRUE(bool(res.expires));
EXPECT_LT(util::now(), *res.expires);
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
ASSERT_TRUE(bool(res.etag));
EXPECT_EQ("snowfall", *res.etag);
@@ -343,13 +383,13 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagNotModified)) {
loop.run();
}
-// Test that we can make a request with etag data that doesn't first try to load
-// from cache like a regular request
+// Test that a network only request doesn't attempt to load data from the cache.
TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagModified)) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
Resource resource { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
+ resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
resource.priorEtag.emplace("sunshine");
using namespace std::chrono_literals;
@@ -368,6 +408,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagModified)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
ASSERT_TRUE(bool(res.etag));
EXPECT_EQ("snowfall", *res.etag);
@@ -377,15 +418,13 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshEtagModified)) {
loop.run();
}
-// Test that we can make a request that doesn't first try to load
-// from cache like a regular request.
+// Test that a network only request doesn't attempt to load data from the cache.
TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheFull)) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
Resource resource { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
- // Setting any prior field results in skipping the cache.
- resource.priorExpires.emplace(Seconds(0));
+ resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
using namespace std::chrono_literals;
@@ -403,6 +442,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheFull)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
ASSERT_TRUE(bool(res.etag));
EXPECT_EQ("snowfall", *res.etag);
@@ -419,6 +459,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedNotModified))
DefaultFileSource fs(":memory:", ".");
Resource resource { Resource::Unknown, "http://127.0.0.1:3000/revalidate-modified" };
+ resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
resource.priorModified.emplace(Seconds(1420070400)); // January 1, 2015
using namespace std::chrono_literals;
@@ -437,6 +478,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedNotModified))
EXPECT_FALSE(res.data.get());
ASSERT_TRUE(bool(res.expires));
EXPECT_LT(util::now(), *res.expires);
+ EXPECT_TRUE(res.mustRevalidate);
ASSERT_TRUE(bool(res.modified));
EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res.modified);
EXPECT_FALSE(bool(res.etag));
@@ -453,6 +495,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedModified)) {
DefaultFileSource fs(":memory:", ".");
Resource resource { Resource::Unknown, "http://127.0.0.1:3000/revalidate-modified" };
+ resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
resource.priorModified.emplace(Seconds(1417392000)); // December 1, 2014
using namespace std::chrono_literals;
@@ -471,6 +514,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(NoCacheRefreshModifiedModified)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_TRUE(res.mustRevalidate);
EXPECT_EQ(Timestamp{ Seconds(1420070400) }, *res.modified);
EXPECT_FALSE(res.etag);
loop.stop();
@@ -502,6 +546,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -518,6 +563,7 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -525,3 +571,81 @@ TEST(DefaultFileSource, TEST_REQUIRES_SERVER(SetResourceTransform)) {
loop.run();
}
+
+// Test that a stale cache file that has must-revalidate set will trigger a response.
+TEST(DefaultFileSource, TEST_REQUIRES_SERVER(RespondToStaleMustRevalidate)) {
+ util::RunLoop loop;
+ DefaultFileSource fs(":memory:", ".");
+
+ Resource resource { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
+ resource.loadingMethod = Resource::LoadingMethod::CacheOnly;
+
+ // using namespace std::chrono_literals;
+
+ // Put an existing value in the cache that has expired, and has must-revalidate set.
+ Response response;
+ response.data = std::make_shared<std::string>("Cached value");
+ response.modified = Timestamp(Seconds(1417392000)); // December 1, 2014
+ response.expires = Timestamp(Seconds(1417392000));
+ response.mustRevalidate = true;
+ response.etag.emplace("snowfall");
+ fs.put(resource, response);
+
+ std::unique_ptr<AsyncRequest> req;
+ req = fs.request(resource, [&](Response res) {
+ req.reset();
+ ASSERT_TRUE(res.error.get());
+ EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason);
+ EXPECT_EQ("Cached resource is unusable", res.error->message);
+ EXPECT_FALSE(res.notModified);
+ ASSERT_TRUE(res.data.get());
+ EXPECT_EQ("Cached value", *res.data);
+ ASSERT_TRUE(res.expires);
+ EXPECT_EQ(Timestamp{ Seconds(1417392000) }, *res.expires);
+ EXPECT_TRUE(res.mustRevalidate);
+ ASSERT_TRUE(res.modified);
+ EXPECT_EQ(Timestamp{ Seconds(1417392000) }, *res.modified);
+ ASSERT_TRUE(res.etag);
+ EXPECT_EQ("snowfall", *res.etag);
+
+ resource.priorEtag = res.etag;
+ resource.priorModified = res.modified;
+ resource.priorExpires = res.expires;
+ resource.priorData = res.data;
+
+ loop.stop();
+ });
+
+ loop.run();
+
+ // Now run this request again, with the data we gathered from the previous stale/unusable
+ // request. We're replacing the data so that we can check that the DefaultFileSource doesn't
+ // attempt another database access if we already have the value.
+ resource.loadingMethod = Resource::LoadingMethod::NetworkOnly;
+ resource.priorData = std::make_shared<std::string>("Prior value");
+
+ req = fs.request(resource, [&](Response res) {
+ req.reset();
+ ASSERT_EQ(nullptr, res.error.get());
+ // Since the data was found in the cache, we're doing a revalidation request. Yet, since
+ // this request hasn't returned data before, we're setting notModified to false in the
+ // OnlineFileSource to ensure that requestors know that this is the first time they're
+ // seeing this data.
+ EXPECT_FALSE(res.notModified);
+ ASSERT_TRUE(res.data.get());
+ // Ensure that it's the value that we manually inserted into the cache rather than the value
+ // the server returns, since we should be executing a revalidation request which doesn't
+ // return new data, only a 304 Not Modified response.
+ EXPECT_EQ("Prior value", *res.data);
+ ASSERT_TRUE(res.expires);
+ EXPECT_LE(util::now(), *res.expires);
+ EXPECT_TRUE(res.mustRevalidate);
+ ASSERT_TRUE(res.modified);
+ EXPECT_EQ(Timestamp{ Seconds(1417392000) }, *res.modified);
+ ASSERT_TRUE(res.etag);
+ EXPECT_EQ("snowfall", *res.etag);
+ loop.stop();
+ });
+
+ loop.run();
+}
diff --git a/test/storage/http_file_source.test.cpp b/test/storage/http_file_source.test.cpp
index 5b081d7d57..006b7a0fb3 100644
--- a/test/storage/http_file_source.test.cpp
+++ b/test/storage/http_file_source.test.cpp
@@ -26,6 +26,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP200)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -44,6 +45,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP404)) {
EXPECT_EQ("HTTP status code 404", res.error->message);
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -61,6 +63,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTPTile404)) {
EXPECT_FALSE(bool(res.error));
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -78,6 +81,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP200EmptyData)) {
EXPECT_FALSE(bool(res.error));
EXPECT_EQ(*res.data, std::string());
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -95,6 +99,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP204)) {
EXPECT_FALSE(bool(res.error));
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -113,6 +118,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP500)) {
EXPECT_EQ("HTTP status code 500", res.error->message);
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -131,6 +137,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(ExpiresParsing)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_EQ(Timestamp{ Seconds(1420797926) }, res.expires);
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_EQ(Timestamp{ Seconds(1420794326) }, res.modified);
EXPECT_EQ("foo", *res.etag);
loop.stop();
@@ -148,6 +155,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(CacheControlParsing)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_GT(Seconds(2), util::abs(*res.expires - util::now() - Seconds(120))) << "Expiration date isn't about 120 seconds in the future";
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -176,6 +184,7 @@ TEST(HTTPFileSource, TEST_REQUIRES_SERVER(Load)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
diff --git a/test/storage/offline.test.cpp b/test/storage/offline.test.cpp
index 0faaabc298..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 });
@@ -52,3 +53,11 @@ TEST(OfflineTilePyramidRegionDefinition, TileCoverWrapped) {
EXPECT_EQ((std::vector<CanonicalTileID>{ { 0, 0, 0 } }),
region.tileCover(SourceType::Vector, 512, { 0, 22 }));
}
+
+TEST(OfflineTilePyramidRegionDefinition, TileCount) {
+ OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 22, 1.0);
+
+ //These numbers match the count from tileCover().size().
+ EXPECT_EQ(38424u, region.tileCount(SourceType::Vector, 512, { 10, 18 }));
+ EXPECT_EQ(9675240u, region.tileCount(SourceType::Vector, 512, { 3, 22 }));
+}
diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp
index 872310e46f..d99c1f946f 100644
--- a/test/storage/offline_database.test.cpp
+++ b/test/storage/offline_database.test.cpp
@@ -634,24 +634,35 @@ static int databaseSyncMode(const std::string& path) {
return stmt.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);
+ const auto sql = std::string("pragma table_info(") + name + ")";
+ mapbox::sqlite::Statement stmt = db.prepare(sql.c_str());
+ std::vector<std::string> columns;
+ while (stmt.run()) {
+ columns.push_back(stmt.get<std::string>(1));
+ }
+ return columns;
+}
+
TEST(OfflineDatabase, MigrateFromV2Schema) {
using namespace mbgl;
// v2.db is a v2 database containing a single offline region with a small number of resources.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v2.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v2.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
- EXPECT_LT(databasePageCount("test/fixtures/offline_database/v5.db"),
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+ EXPECT_LT(databasePageCount("test/fixtures/offline_database/migrated.db"),
databasePageCount("test/fixtures/offline_database/v2.db"));
}
@@ -660,18 +671,18 @@ TEST(OfflineDatabase, MigrateFromV3Schema) {
// v3.db is a v3 database, migrated from v2.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v3.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v3.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
}
TEST(OfflineDatabase, MigrateFromV4Schema) {
@@ -679,22 +690,74 @@ TEST(OfflineDatabase, MigrateFromV4Schema) {
// v4.db is a v4 database, migrated from v2 & v3. This database used `journal_mode = WAL` and `synchronous = NORMAL`.
- deleteFile("test/fixtures/offline_database/v5.db");
- writeFile("test/fixtures/offline_database/v5.db", util::read_file("test/fixtures/offline_database/v4.db"));
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v4.db"));
{
- OfflineDatabase db("test/fixtures/offline_database/v5.db", 0);
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
auto regions = db.listRegions();
for (auto& region : regions) {
db.deleteRegion(std::move(region));
}
}
- EXPECT_EQ(5, databaseUserVersion("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
// Journal mode should be DELETE after migration to v5.
- EXPECT_EQ("delete", databaseJournalMode("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ("delete", databaseJournalMode("test/fixtures/offline_database/migrated.db"));
// Synchronous setting should be FULL (2) after migration to v5.
- EXPECT_EQ(2, databaseSyncMode("test/fixtures/offline_database/v5.db"));
+ EXPECT_EQ(2, databaseSyncMode("test/fixtures/offline_database/migrated.db"));
+}
+
+
+TEST(OfflineDatabase, MigrateFromV5Schema) {
+ using namespace mbgl;
+
+ // v5.db is a v5 database, migrated from v2, v3 & v4.
+
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v5.db"));
+
+ {
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
+ auto regions = db.listRegions();
+ for (auto& region : regions) {
+ db.deleteRegion(std::move(region));
+ }
+ }
+
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+
+ EXPECT_EQ((std::vector<std::string>{ "id", "url_template", "pixel_ratio", "z", "x", "y",
+ "expires", "modified", "etag", "data", "compressed",
+ "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "tiles"));
+ EXPECT_EQ((std::vector<std::string>{ "id", "url", "kind", "expires", "modified", "etag", "data",
+ "compressed", "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "resources"));
+}
+
+TEST(OfflineDatabase, DowngradeSchema) {
+ using namespace mbgl;
+
+ // v999.db is a v999 database, it should be deleted
+ // and recreated with the current schema.
+
+ deleteFile("test/fixtures/offline_database/migrated.db");
+ writeFile("test/fixtures/offline_database/migrated.db", util::read_file("test/fixtures/offline_database/v999.db"));
+
+ {
+ OfflineDatabase db("test/fixtures/offline_database/migrated.db", 0);
+ }
+
+ EXPECT_EQ(6, databaseUserVersion("test/fixtures/offline_database/migrated.db"));
+
+ EXPECT_EQ((std::vector<std::string>{ "id", "url_template", "pixel_ratio", "z", "x", "y",
+ "expires", "modified", "etag", "data", "compressed",
+ "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "tiles"));
+ EXPECT_EQ((std::vector<std::string>{ "id", "url", "kind", "expires", "modified", "etag", "data",
+ "compressed", "accessed", "must_revalidate" }),
+ databaseTableColumns("test/fixtures/offline_database/migrated.db", "resources"));
}
diff --git a/test/storage/online_file_source.test.cpp b/test/storage/online_file_source.test.cpp
index 1a1d2d42f8..70bfe3ac95 100644
--- a/test/storage/online_file_source.test.cpp
+++ b/test/storage/online_file_source.test.cpp
@@ -36,6 +36,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(CancelMultiple)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -62,6 +63,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(TemporaryError)) {
EXPECT_EQ("HTTP status code 500", res.error->message);
ASSERT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
} break;
@@ -73,6 +75,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(TemporaryError)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -99,6 +102,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(ConnectionError)) {
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
ASSERT_FALSE(res.data.get());
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
@@ -126,6 +130,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(Timeout)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_TRUE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (counter == 4) {
@@ -150,6 +155,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RetryDelayOnExpiredTile)) {
counter++;
EXPECT_EQ(nullptr, res.error);
EXPECT_GT(util::now(), *res.expires);
+ EXPECT_FALSE(res.mustRevalidate);
});
util::Timer timer;
@@ -170,6 +176,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RetryOnClockSkew)) {
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/clockskew" };
std::unique_ptr<AsyncRequest> req1 = fs.request(resource, [&](Response res) {
+ EXPECT_FALSE(res.mustRevalidate);
switch (counter++) {
case 0: {
EXPECT_EQ(nullptr, res.error);
@@ -240,6 +247,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(Load)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
@@ -277,6 +285,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusChange)) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
@@ -315,6 +324,7 @@ TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusChangePreempt)) {
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
ASSERT_FALSE(res.data.get());
EXPECT_FALSE(bool(res.expires));
+ EXPECT_FALSE(res.mustRevalidate);
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
diff --git a/test/storage/resource.test.cpp b/test/storage/resource.test.cpp
index 5a27aa98a5..083134c4d0 100644
--- a/test/storage/resource.test.cpp
+++ b/test/storage/resource.test.cpp
@@ -115,6 +115,10 @@ TEST(Resource, SpriteImage) {
Resource resource = Resource::spriteImage("http://example.com/sprite", 2.0);
EXPECT_EQ(Resource::Kind::SpriteImage, resource.kind);
EXPECT_EQ("http://example.com/sprite@2x.png", resource.url);
+
+ Resource paramResource = Resource::spriteImage("http://example.com/sprite?query=true", 2.0);
+ EXPECT_EQ(Resource::Kind::SpriteImage, paramResource.kind);
+ EXPECT_EQ("http://example.com/sprite@2x.png?query=true", paramResource.url);
}
TEST(Resource, Image) {
@@ -129,4 +133,8 @@ TEST(Resource, SpriteJSON) {
Resource resource = Resource::spriteJSON("http://example.com/sprite", 2.0);
EXPECT_EQ(Resource::Kind::SpriteJSON, resource.kind);
EXPECT_EQ("http://example.com/sprite@2x.json", resource.url);
+
+ Resource paramResource = Resource::spriteJSON("http://example.com/sprite?query=true", 2.0);
+ EXPECT_EQ(Resource::Kind::SpriteJSON, paramResource.kind);
+ EXPECT_EQ("http://example.com/sprite@2x.json?query=true", paramResource.url);
}
diff --git a/test/storage/server.js b/test/storage/server.js
index b54ff835ec..d6429e4635 100755
--- a/test/storage/server.js
+++ b/test/storage/server.js
@@ -44,8 +44,8 @@ app.get('/cache', function(req, res) {
app.get('/revalidate-same', function(req, res) {
if (req.headers['if-none-match'] == 'snowfall') {
- // Second request can be cached for 30 seconds.
- res.setHeader('Cache-Control', 'max-age=30');
+ // Second request can be cached for 1 second.
+ res.setHeader('Cache-Control', 'max-age=1, must-revalidate');
res.status(304).end();
} else {
// First request must always be revalidated.
@@ -67,7 +67,7 @@ app.get('/revalidate-modified', function(req, res) {
if (req.headers['if-modified-since']) {
var modified_since = new Date(req.headers['if-modified-since']);
if (modified_since >= jan1) {
- res.setHeader('Cache-Control', 'max-age=30');
+ res.setHeader('Cache-Control', 'max-age=1, must-revalidate');
res.status(304).end();
return;
}
diff --git a/test/storage/sqlite.test.cpp b/test/storage/sqlite.test.cpp
index dbd7a09868..36715a2fd0 100644
--- a/test/storage/sqlite.test.cpp
+++ b/test/storage/sqlite.test.cpp
@@ -25,3 +25,14 @@ TEST(SQLite, Statement) {
ASSERT_EQ(stmt2.lastInsertRowId(), 2);
ASSERT_EQ(stmt2.changes(), 1u);
}
+
+TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) {
+ try {
+ // Should throw a CANTOPEN when the database doesn't exist,
+ // make sure all the backends behave the same way.
+ 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);
+ }
+}
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 e6bd984f36..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,29 +9,26 @@ 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
+ ASSERT_EQ(converted.minzoom, defaults.minzoom);
ASSERT_EQ(converted.maxzoom, defaults.maxzoom);
ASSERT_EQ(converted.buffer, defaults.buffer);
ASSERT_EQ(converted.tolerance, defaults.tolerance);
@@ -43,24 +39,19 @@ 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);
ASSERT_EQ(converted.maxzoom, 1);
ASSERT_EQ(converted.buffer, 2);
ASSERT_EQ(converted.tolerance, 3);
diff --git a/test/style/conversion/layer.test.cpp b/test/style/conversion/layer.test.cpp
index d51d7d33e2..33cd329999 100644
--- a/test/style/conversion/layer.test.cpp
+++ b/test/style/conversion/layer.test.cpp
@@ -1,10 +1,8 @@
#include <mbgl/test/util.hpp>
-#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/layers/background_layer_impl.hpp>
-#include <mbgl/util/rapidjson.hpp>
using namespace mbgl;
using namespace mbgl::style;
@@ -12,10 +10,8 @@ using namespace mbgl::style::conversion;
using namespace std::literals::chrono_literals;
std::unique_ptr<Layer> parseLayer(const std::string& src) {
- JSDocument doc;
- doc.Parse<0>(src);
Error error;
- return std::move(*convert<std::unique_ptr<Layer>, JSValue>(doc, error));
+ return std::move(*convertJSON<std::unique_ptr<Layer>>(src, error));
}
TEST(StyleConversion, LayerTransition) {
diff --git a/test/style/conversion/light.test.cpp b/test/style/conversion/light.test.cpp
index 28e22b3550..67e48c942e 100644
--- a/test/style/conversion/light.test.cpp
+++ b/test/style/conversion/light.test.cpp
@@ -1,11 +1,10 @@
#include <mbgl/test/util.hpp>
#include <mbgl/style/conversion.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/light.hpp>
#include <mbgl/style/position.hpp>
-#include <mbgl/util/rapidjson.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/chrono.hpp>
@@ -19,9 +18,7 @@ TEST(StyleConversion, Light) {
Error error;
auto parseLight = [&](const std::string& src) {
- JSDocument doc;
- doc.Parse<0>(src);
- return convert<Light>(doc, error);
+ return convertJSON<Light>(src, error);
};
{
diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp
new file mode 100644
index 0000000000..694569695c
--- /dev/null
+++ b/test/style/expression/expression.test.cpp
@@ -0,0 +1,91 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/rapidjson.hpp>
+#include <mbgl/style/rapidjson_conversion.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+
+#include <rapidjson/document.h>
+
+#include <iostream>
+#include <fstream>
+#include <dirent.h>
+
+
+using namespace mbgl;
+using namespace mbgl::style;
+
+TEST(Expression, IsExpression) {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> spec;
+ spec.Parse<0>(util::read_file("mapbox-gl-js/src/style-spec/reference/v8.json").c_str());
+ ASSERT_FALSE(spec.HasParseError());
+ ASSERT_TRUE(spec.IsObject() &&
+ spec.HasMember("expression_name") &&
+ spec["expression_name"].IsObject() &&
+ spec["expression_name"].HasMember("values") &&
+ spec["expression_name"]["values"].IsObject());
+
+ const auto& allExpressions = spec["expression_name"]["values"];
+
+ for(auto& entry : allExpressions.GetObject()) {
+ const std::string name { entry.name.GetString(), entry.name.GetStringLength() };
+ JSDocument document;
+ document.Parse<0>(R"([")" + name + R"("])");
+
+ const JSValue* expression = &document;
+ EXPECT_TRUE(expression::isExpression(conversion::Convertible(expression))) << name;
+ }
+}
+
+class ExpressionEqualityTest : public ::testing::TestWithParam<std::string> {};
+
+TEST_P(ExpressionEqualityTest, ExpressionEquality) {
+ const std::string base = std::string("test/fixtures/expression_equality/") + GetParam();
+
+ std::string error;
+ auto parse = [&](std::string filename, std::string& error_) -> std::unique_ptr<expression::Expression> {
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> document;
+ document.Parse<0>(util::read_file(filename).c_str());
+ assert(!document.HasParseError());
+ const JSValue* expression = &document;
+ expression::ParsingContext ctx;
+ expression::ParseResult parsed = ctx.parse(conversion::Convertible(expression));
+ if (!parsed) {
+ error_ = ctx.getErrors().size() > 0 ? ctx.getErrors()[0].message : "failed to parse";
+ };
+ return std::move(*parsed);
+ };
+
+ std::unique_ptr<expression::Expression> expression_a1 = parse(base + ".a.json", error);
+ ASSERT_TRUE(expression_a1) << GetParam() << ": " << error;
+
+ std::unique_ptr<expression::Expression> expression_a2 = parse(base + ".a.json", error);
+ ASSERT_TRUE(expression_a2) << GetParam() << ": " << error;
+
+ std::unique_ptr<expression::Expression> expression_b = parse(base + ".b.json", error);
+ ASSERT_TRUE(expression_b) << GetParam() << ": " << error;
+
+
+ EXPECT_TRUE(*expression_a1 == *expression_a2);
+ EXPECT_TRUE(*expression_a1 != *expression_b);
+}
+
+INSTANTIATE_TEST_CASE_P(Expression, ExpressionEqualityTest, ::testing::ValuesIn([] {
+ std::vector<std::string> names;
+ const std::string ending = ".a.json";
+
+ const std::string style_directory = "test/fixtures/expression_equality";
+ DIR *dir = opendir(style_directory.c_str());
+ if (dir != nullptr) {
+ for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) {
+ const std::string name = dp->d_name;
+ if (name.length() >= ending.length() && name.compare(name.length() - ending.length(), ending.length(), ending) == 0) {
+ names.push_back(name.substr(0, name.length() - ending.length()));
+ }
+ }
+ closedir(dir);
+ }
+
+ EXPECT_GT(names.size(), 0u);
+ return names;
+}()));
diff --git a/test/style/expression/util.test.cpp b/test/style/expression/util.test.cpp
new file mode 100644
index 0000000000..0337cd871f
--- /dev/null
+++ b/test/style/expression/util.test.cpp
@@ -0,0 +1,23 @@
+#include <mbgl/test/util.hpp>
+#include <mbgl/style/expression/util.hpp>
+
+using namespace mbgl;
+using namespace mbgl::style::expression;
+
+TEST(Expression, Util_rgba) {
+ Result<Color> valid = rgba(0, 0, 0, 0);
+ ASSERT_TRUE(valid);
+ ASSERT_EQ(valid->r, 0);
+ ASSERT_EQ(valid->g, 0);
+ ASSERT_EQ(valid->b, 0);
+ ASSERT_EQ(valid->a, 0);
+
+ ASSERT_FALSE(rgba(0, 0, 0, -0.1));
+ ASSERT_FALSE(rgba(0, 0, 0, 1.1));
+ ASSERT_FALSE(rgba(0, 0, -1, 1));
+ ASSERT_FALSE(rgba(0, 0, 256, 1));
+ ASSERT_FALSE(rgba(0, -1, 0, 1));
+ ASSERT_FALSE(rgba(0, 256, 0, 1));
+ ASSERT_FALSE(rgba(-1, 1, 0, 1));
+ ASSERT_FALSE(rgba(-256, 1, 0, 1));
+}
diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp
index 96de125945..73f8e7626d 100644
--- a/test/style/filter.test.cpp
+++ b/test/style/filter.test.cpp
@@ -4,20 +4,15 @@
#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
-#include <mbgl/style/rapidjson_conversion.hpp>
-#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/json.hpp>
#include <mbgl/style/conversion/filter.hpp>
-#include <rapidjson/document.h>
-
using namespace mbgl;
using namespace mbgl::style;
Filter parse(const char * expression) {
- rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc;
- doc.Parse<0>(expression);
conversion::Error error;
- optional<Filter> filter = conversion::convert<Filter, JSValue>(doc, error);
+ optional<Filter> filter = conversion::convertJSON<Filter>(expression, error);
EXPECT_TRUE(bool(filter));
return *filter;
}
diff --git a/test/style/function/source_function.test.cpp b/test/style/function/source_function.test.cpp
index 260620c8d0..46ad961002 100644
--- a/test/style/function/source_function.test.cpp
+++ b/test/style/function/source_function.test.cpp
@@ -76,11 +76,14 @@ TEST(SourceFunction, Categorical) {
EXPECT_EQ(0.0f, SourceFunction<float>("property", CategoricalStops<float>({{ int64_t(1), 1.0f }}))
.evaluate(oneString, 0.0f));
- EXPECT_EQ(0.0f, SourceFunction<float>("property", CategoricalStops<float>({{ "1"s, 1.0f }}))
+ CategoricalStops<float>::Stops stops;
+ stops["1"s] = 1.0f;
+
+ EXPECT_EQ(0.0f, SourceFunction<float>("property", CategoricalStops<float>(stops))
.evaluate(oneInteger, 0.0f));
- EXPECT_EQ(0.0f, SourceFunction<float>("property", CategoricalStops<float>({{ "1"s, 1.0f }}))
+ EXPECT_EQ(0.0f, SourceFunction<float>("property", CategoricalStops<float>(stops))
.evaluate(oneDouble, 0.0f));
- EXPECT_EQ(1.0f, SourceFunction<float>("property", CategoricalStops<float>({{ "1"s, 1.0f }}))
+ EXPECT_EQ(1.0f, SourceFunction<float>("property", CategoricalStops<float>(stops))
.evaluate(oneString, 0.0f));
EXPECT_EQ(1.0f, SourceFunction<float>("property", CategoricalStops<float>({{ true, 1.0f }}))
diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp
index eaa3c72877..eb419e8080 100644
--- a/test/style/source.test.cpp
+++ b/test/style/source.test.cpp
@@ -3,11 +3,13 @@
#include <mbgl/test/stub_style_observer.hpp>
#include <mbgl/test/stub_render_source_observer.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/source_impl.hpp>
#include <mbgl/style/sources/raster_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/raster_layer.cpp>
#include <mbgl/style/layers/line_layer.hpp>
@@ -23,7 +25,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>
@@ -37,6 +39,7 @@
#include <cstdint>
using namespace mbgl;
+using SourceType = mbgl::style::SourceType;
class SourceTest {
public:
@@ -47,7 +50,8 @@ public:
Transform transform;
TransformState transformState;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager;
+ Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
ImageManager imageManager;
GlyphManager glyphManager { fileSource };
@@ -60,7 +64,8 @@ public:
MapMode::Continuous,
annotationManager,
imageManager,
- glyphManager
+ glyphManager,
+ 0
};
SourceTest() {
@@ -530,11 +535,11 @@ TEST(Source, ImageSourceImageUpdate) {
// Load initial, so the source state will be loaded=true
source.loadDescription(test.fileSource);
- UnassociatedImage rgba({ 1, 1 });
+ PremultipliedImage rgba({ 1, 1 });
rgba.data[0] = 255;
rgba.data[1] = 254;
rgba.data[2] = 253;
- rgba.data[3] = 128;
+ rgba.data[3] = 0;
// Schedule an update
test.loop.invoke([&] () {
@@ -544,3 +549,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.test.cpp b/test/style/style.test.cpp
index ab58eb1024..9bdab37ac6 100644
--- a/test/style/style.test.cpp
+++ b/test/style/style.test.cpp
@@ -28,27 +28,27 @@ TEST(Style, Properties) {
style.loadJSON(R"STYLE({"center": [10, 20]})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ((LatLng{20, 10}), style.getDefaultLatLng());
+ ASSERT_EQ((LatLng{20, 10}), *style.getDefaultCamera().center);
style.loadJSON(R"STYLE({"bearing": 24})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ((LatLng{0, 0}), style.getDefaultLatLng());
- ASSERT_EQ(24, style.getDefaultBearing());
+ ASSERT_EQ(LatLng {}, *style.getDefaultCamera().center);
+ ASSERT_EQ(24, *style.getDefaultCamera().angle);
style.loadJSON(R"STYLE({"zoom": 13.3})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ(13.3, style.getDefaultZoom());
+ ASSERT_EQ(13.3, *style.getDefaultCamera().zoom);
style.loadJSON(R"STYLE({"pitch": 60})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ(60, style.getDefaultPitch());
+ ASSERT_EQ(60, *style.getDefaultCamera().pitch);
style.loadJSON(R"STYLE({"name": 23, "center": {}, "bearing": "north", "zoom": null, "pitch": "wide"})STYLE");
ASSERT_EQ("", style.getName());
- ASSERT_EQ((LatLng{0, 0}), style.getDefaultLatLng());
- ASSERT_EQ(0, style.getDefaultBearing());
- ASSERT_EQ(0, style.getDefaultZoom());
- ASSERT_EQ(0, style.getDefaultPitch());
+ ASSERT_EQ(LatLng {}, *style.getDefaultCamera().center);
+ ASSERT_EQ(0, *style.getDefaultCamera().zoom);
+ ASSERT_EQ(0, *style.getDefaultCamera().angle);
+ ASSERT_EQ(0, *style.getDefaultCamera().pitch);
}
TEST(Style, DuplicateSource) {
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..b40827d63e
--- /dev/null
+++ b/test/text/cross_tile_symbol_index.test.cpp
@@ -0,0 +1,85 @@
+#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;
+ CrossTileSymbolLayerIndex index;
+
+ style::SymbolLayoutProperties::PossiblyEvaluated layout;
+ bool sdfIcons = false;
+ bool iconsNeedLinear = false;
+ bool sortFeaturesByY = false;
+
+
+ OverscaledTileID mainID(6, 0, 6, 8, 8);
+ std::vector<SymbolInstance> mainInstances;
+ mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit"));
+ mainInstances.push_back(makeSymbolInstance(2000, 2000, u"Toronto"));
+ SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) };
+ index.addBucket(mainID, mainBucket, maxCrossTileID);
+
+ // Assigned new IDs
+ ASSERT_EQ(mainBucket.symbolInstances.at(0).crossTileID, 1u);
+ ASSERT_EQ(mainBucket.symbolInstances.at(1).crossTileID, 2u);
+
+
+ OverscaledTileID childID(7, 0, 7, 16, 16);
+ std::vector<SymbolInstance> childInstances;
+ childInstances.push_back(makeSymbolInstance(2000, 2000, u"Detroit"));
+ childInstances.push_back(makeSymbolInstance(2000, 2000, u"Windsor"));
+ childInstances.push_back(makeSymbolInstance(3000, 3000, u"Toronto"));
+ childInstances.push_back(makeSymbolInstance(4001, 4001, u"Toronto"));
+ SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) };
+ index.addBucket(childID, childBucket, maxCrossTileID);
+
+ // matched parent tile
+ ASSERT_EQ(childBucket.symbolInstances.at(0).crossTileID, 1u);
+ // does not match because of different key
+ ASSERT_EQ(childBucket.symbolInstances.at(1).crossTileID, 3u);
+ // does not match because of different location
+ ASSERT_EQ(childBucket.symbolInstances.at(2).crossTileID, 4u);
+ // matches with a slightly different location
+ ASSERT_EQ(childBucket.symbolInstances.at(3).crossTileID, 2u);
+
+ OverscaledTileID parentID(5, 0, 5, 4, 4);
+ std::vector<SymbolInstance> parentInstances;
+ parentInstances.push_back(makeSymbolInstance(500, 500, u"Detroit"));
+ SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(parentInstances) };
+ index.addBucket(parentID, parentBucket, maxCrossTileID);
+
+ // matched child tile
+ ASSERT_EQ(parentBucket.symbolInstances.at(0).crossTileID, 1u);
+
+ std::unordered_set<uint32_t> currentIDs;
+ currentIDs.insert(mainBucket.bucketInstanceId);
+ index.removeStaleBuckets(currentIDs);
+
+ // grandchild
+ OverscaledTileID grandchildID(8, 0, 8, 32, 32);
+ std::vector<SymbolInstance> grandchildInstances;
+ grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Detroit"));
+ grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Windsor"));
+ SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(grandchildInstances) };
+ 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);
+
+}
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/text/quads.test.cpp b/test/text/quads.test.cpp
index efc3912aaa..8eedd9bd2e 100644
--- a/test/text/quads.test.cpp
+++ b/test/text/quads.test.cpp
@@ -13,20 +13,18 @@ TEST(getIconQuads, normal) {
SymbolLayoutProperties::Evaluated layout;
Anchor anchor(2.0, 3.0, 0.0, 0.5f, 0);
ImagePosition image = {
- mapbox::Bin(-1, 15, 11, 0, 0),
+ mapbox::Bin(-1, 15, 11, 0, 0, 0, 0),
style::Image::Impl("test", PremultipliedImage({1,1}), 1.0)
};
- auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -6.5f, -4.5f }}, 0);
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -6.5f, -4.5f }}, SymbolAnchorType::Center, 0);
GeometryCoordinates line;
Shaping shapedText;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 16.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 16.0f, shapedText);
- EXPECT_EQ(quad.anchorPoint.x, 2);
- EXPECT_EQ(quad.anchorPoint.y, 3);
EXPECT_EQ(quad.tl.x, -14);
EXPECT_EQ(quad.tl.y, -10);
EXPECT_EQ(quad.tr.x, 1);
@@ -35,19 +33,16 @@ TEST(getIconQuads, normal) {
EXPECT_EQ(quad.bl.y, 1);
EXPECT_EQ(quad.br.x, 1);
EXPECT_EQ(quad.br.y, 1);
- EXPECT_EQ(quad.anchorAngle, 0.0f);
- EXPECT_EQ(quad.glyphAngle, 0.0f);
- EXPECT_EQ(quad.minScale, 0.5f);
}
TEST(getIconQuads, style) {
Anchor anchor(0.0, 0.0, 0.0, 0.5f, 0);
ImagePosition image = {
- mapbox::Bin(-1, 20, 20, 0, 0),
+ mapbox::Bin(-1, 20, 20, 0, 0, 0, 0),
style::Image::Impl("test", PremultipliedImage({1,1}), 1.0)
};
- auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, 0);
+ auto shapedIcon = PositionedIcon::shapeIcon(image, {{ -9.5f, -9.5f }}, SymbolAnchorType::Center, 0);
GeometryCoordinates line;
Shaping shapedText;
@@ -55,16 +50,14 @@ TEST(getIconQuads, style) {
shapedText.bottom = 30.0f;
shapedText.left = -60.0f;
shapedText.right = 20.0f;
- shapedText.positionedGlyphs.emplace_back(PositionedGlyph(32, 0.0f, 0.0f, 0));
+ shapedText.positionedGlyphs.emplace_back(PositionedGlyph(32, 0.0f, 0.0f, false));
// none
{
SymbolLayoutProperties::Evaluated layout;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
- EXPECT_EQ(quad.anchorPoint.x, 0);
- EXPECT_EQ(quad.anchorPoint.y, 0);
EXPECT_EQ(quad.tl.x, -19.5);
EXPECT_EQ(quad.tl.y, -19.5);
EXPECT_EQ(quad.tr.x, 0.5);
@@ -73,9 +66,6 @@ TEST(getIconQuads, style) {
EXPECT_EQ(quad.bl.y, 0.5);
EXPECT_EQ(quad.br.x, 0.5);
EXPECT_EQ(quad.br.y, 0.5);
- EXPECT_EQ(quad.anchorAngle, 0.0f);
- EXPECT_EQ(quad.glyphAngle, 0.0f);
- EXPECT_EQ(quad.minScale, 0.5f);
}
// width
@@ -84,7 +74,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Width;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 24.0f, shapedText);
EXPECT_EQ(quad.tl.x, -60);
EXPECT_EQ(quad.tl.y, 0);
@@ -102,7 +92,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Width;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
EXPECT_EQ(quad.tl.x, -30);
EXPECT_EQ(quad.tl.y, -5);
@@ -124,7 +114,7 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
EXPECT_EQ(quad.tl.x, -40);
EXPECT_EQ(quad.tl.y, -10);
@@ -142,7 +132,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Height;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 24.0f, shapedText);
EXPECT_EQ(quad.tl.x, -30);
EXPECT_EQ(quad.tl.y, -10);
@@ -160,7 +150,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Height;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
EXPECT_EQ(quad.tl.x, -20);
EXPECT_EQ(quad.tl.y, -5);
@@ -182,7 +172,7 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
EXPECT_EQ(quad.tl.x, -30);
EXPECT_EQ(quad.tl.y, -10);
@@ -200,7 +190,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 24.0f;
layout.get<IconTextFit>() = IconTextFitType::Both;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 24.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 24.0f, shapedText);
EXPECT_EQ(quad.tl.x, -60);
EXPECT_EQ(quad.tl.y, -10);
@@ -218,7 +208,7 @@ TEST(getIconQuads, style) {
layout.get<TextSize>() = 12.0f;
layout.get<IconTextFit>() = IconTextFitType::Both;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
EXPECT_EQ(quad.tl.x, -30);
EXPECT_EQ(quad.tl.y, -5);
@@ -240,7 +230,7 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[2] = 5.0f;
layout.get<IconTextFitPadding>()[3] = 10.0f;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
EXPECT_EQ(quad.tl.x, -40);
EXPECT_EQ(quad.tl.y, -10);
@@ -262,7 +252,7 @@ TEST(getIconQuads, style) {
layout.get<IconTextFitPadding>()[2] = 10.0f;
layout.get<IconTextFitPadding>()[3] = 15.0f;
SymbolQuad quad =
- getIconQuad(anchor, shapedIcon, line, layout, 12.0f, SymbolPlacementType::Point, shapedText);
+ getIconQuad(shapedIcon, layout, 12.0f, shapedText);
EXPECT_EQ(quad.tl.x, -45);
EXPECT_EQ(quad.tl.y, -5);
diff --git a/test/tile/annotation_tile.test.cpp b/test/tile/annotation_tile.test.cpp
deleted file mode 100644
index 6d00a3236a..0000000000
--- a/test/tile/annotation_tile.test.cpp
+++ /dev/null
@@ -1,98 +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/map/query.hpp>
-#include <mbgl/renderer/render_style.hpp>
-#include <mbgl/renderer/tile_parameters.hpp>
-#include <mbgl/map/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/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-
-#include <memory>
-
-using namespace mbgl;
-
-class AnnotationTileTest {
-public:
- FakeFileSource fileSource;
- TransformState transformState;
- util::RunLoop loop;
- ThreadPool threadPool { 1 };
- AnnotationManager annotationManager;
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- RenderStyle style { threadPool, fileSource };
- ImageManager imageManager;
- GlyphManager glyphManager { fileSource };
-
- TileParameters tileParameters {
- 1.0,
- MapDebugOptions(),
- transformState,
- threadPool,
- fileSource,
- MapMode::Continuous,
- annotationManager,
- imageManager,
- glyphManager
- };
-};
-
-// 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::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::move(collisionTile),
- {},
- {},
- 0
- });
-
- // Simulate a second layout with empty data.
- tile.onLayout(GeometryTile::LayoutResult {
- {},
- 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, test.style, 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 2aa85c3860..c05e04bc8d 100644
--- a/test/tile/geojson_tile.test.cpp
+++ b/test/tile/geojson_tile.test.cpp
@@ -8,6 +8,7 @@
#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>
@@ -24,7 +25,8 @@ public:
TransformState transformState;
util::RunLoop loop;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager;
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
ImageManager imageManager;
GlyphManager glyphManager { fileSource };
Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" };
@@ -38,7 +40,8 @@ public:
MapMode::Continuous,
annotationManager,
imageManager,
- glyphManager
+ glyphManager,
+ 0
};
};
@@ -63,7 +66,6 @@ TEST(GeoJSONTile, Issue7648) {
tile.setLayers({{ layer.baseImpl }});
tile.setObserver(&observer);
- tile.setPlacementConfig({});
while (!tile.isComplete()) {
test.loop.runOnce();
@@ -74,3 +76,40 @@ TEST(GeoJSONTile, Issue7648) {
test.loop.runOnce();
}
}
+
+// Tests that tiles remain renderable if they have been renderable and then had an error sent to
+// them, e.g. when revalidating/refreshing the request.
+TEST(GeoJSONTile, Issue9927) {
+ GeoJSONTileTest test;
+
+ CircleLayer layer("circle", "source");
+
+ mapbox::geometry::feature_collection<int16_t> features;
+ features.push_back(mapbox::geometry::feature<int16_t> {
+ mapbox::geometry::point<int16_t>(0, 0)
+ });
+
+ GeoJSONTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, features);
+
+ tile.setLayers({{ layer.baseImpl }});
+
+ while (!tile.isComplete()) {
+ test.loop.runOnce();
+ }
+
+ ASSERT_TRUE(tile.isRenderable());
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
+
+ // 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")));
+ ASSERT_TRUE(tile.isRenderable());
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
+
+ // Then simulate a parsing failure and make sure that we keep it renderable in this situation
+ // as well. We're using 3 as a correlationID since we've done two three calls that increment
+ // this counter (as part of the GeoJSONTile constructor, setLayers, and setPlacementConfig).
+ tile.onError(std::make_exception_ptr(std::runtime_error("Parse error")), 3);
+ ASSERT_TRUE(tile.isRenderable());
+ ASSERT_NE(nullptr, tile.getBucket(*layer.baseImpl));
+ }
diff --git a/test/tile/raster_tile.test.cpp b/test/tile/raster_tile.test.cpp
index a0666c2146..8b2b3dee61 100644
--- a/test/tile/raster_tile.test.cpp
+++ b/test/tile/raster_tile.test.cpp
@@ -3,6 +3,7 @@
#include <mbgl/tile/raster_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>
@@ -20,7 +21,8 @@ public:
TransformState transformState;
util::RunLoop loop;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager;
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
ImageManager imageManager;
GlyphManager glyphManager { fileSource };
Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" };
@@ -34,7 +36,8 @@ public:
MapMode::Continuous,
annotationManager,
imageManager,
- glyphManager
+ glyphManager,
+ 0
};
};
@@ -50,7 +53,7 @@ TEST(RasterTile, setError) {
TEST(RasterTile, onError) {
RasterTileTest test;
RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
- tile.onError(std::make_exception_ptr(std::runtime_error("test")));
+ tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0);
EXPECT_FALSE(tile.isRenderable());
EXPECT_TRUE(tile.isLoaded());
EXPECT_TRUE(tile.isComplete());
@@ -59,16 +62,30 @@ TEST(RasterTile, onError) {
TEST(RasterTile, onParsed) {
RasterTileTest test;
RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
- tile.onParsed(std::make_unique<RasterBucket>(UnassociatedImage{}));
+ tile.onParsed(std::make_unique<RasterBucket>(PremultipliedImage{}), 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(RasterTile, onParsedEmpty) {
RasterTileTest test;
RasterTile tile(OverscaledTileID(0, 0, 0), test.tileParameters, test.tileset);
- tile.onParsed(nullptr);
+ tile.onParsed(nullptr, 0);
EXPECT_FALSE(tile.isRenderable());
EXPECT_TRUE(tile.isLoaded());
EXPECT_TRUE(tile.isComplete());
diff --git a/test/tile/tile_id.test.cpp b/test/tile/tile_id.test.cpp
index 1ef19fea0e..2f328b78d7 100644
--- a/test/tile/tile_id.test.cpp
+++ b/test/tile/tile_id.test.cpp
@@ -127,17 +127,17 @@ TEST(TileID, Canonical) {
TEST(TileID, Overscaled) {
EXPECT_TRUE(OverscaledTileID(4, 2, 3) == OverscaledTileID(4, 2, 3));
EXPECT_FALSE(OverscaledTileID(4, 2, 3) != OverscaledTileID(4, 2, 3));
- EXPECT_TRUE(OverscaledTileID(4, { 4, 2, 3 }) == OverscaledTileID(4, 2, 3));
- EXPECT_FALSE(OverscaledTileID(4, { 4, 2, 3 }) != OverscaledTileID(4, 2, 3));
- EXPECT_TRUE(OverscaledTileID(4, 2, 3) == OverscaledTileID(4, { 4, 2, 3 }));
- EXPECT_FALSE(OverscaledTileID(4, 2, 3) != OverscaledTileID(4, { 4, 2, 3 }));
-
- EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(5, { 4, 2, 3 }));
- EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(5, { 4, 2, 3 }));
- EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(6, { 4, 2, 3 }));
- EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(6, { 4, 2, 3 }));
- EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(7, { 4, 2, 3 }));
- EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(7, { 4, 2, 3 }));
+ EXPECT_TRUE(OverscaledTileID(4, 0, { 4, 2, 3 }) == OverscaledTileID(4, 2, 3));
+ EXPECT_FALSE(OverscaledTileID(4, 0, { 4, 2, 3 }) != OverscaledTileID(4, 2, 3));
+ EXPECT_TRUE(OverscaledTileID(4, 2, 3) == OverscaledTileID(4, 0, { 4, 2, 3 }));
+ EXPECT_FALSE(OverscaledTileID(4, 2, 3) != OverscaledTileID(4, 0, { 4, 2, 3 }));
+
+ EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(5, 0, { 4, 2, 3 }));
+ EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(5, 0, { 4, 2, 3 }));
+ EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(6, 0, { 4, 2, 3 }));
+ EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(6, 0, { 4, 2, 3 }));
+ EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(7, 0, { 4, 2, 3 }));
+ EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(7, 0, { 4, 2, 3 }));
EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(4, 2, 4));
EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(4, 2, 4));
@@ -146,70 +146,70 @@ TEST(TileID, Overscaled) {
EXPECT_TRUE(OverscaledTileID(4, 2, 3) != OverscaledTileID(5, 2, 3));
EXPECT_FALSE(OverscaledTileID(4, 2, 3) == OverscaledTileID(5, 2, 3));
- EXPECT_TRUE(OverscaledTileID(7, { 4, 2, 3 }) == OverscaledTileID(7, { 4, 2, 3 }));
- EXPECT_FALSE(OverscaledTileID(7, { 4, 2, 3 }) != OverscaledTileID(7, { 4, 2, 3 }));
+ EXPECT_TRUE(OverscaledTileID(7, 0, { 4, 2, 3 }) == OverscaledTileID(7, 0, { 4, 2, 3 }));
+ EXPECT_FALSE(OverscaledTileID(7, 0, { 4, 2, 3 }) != OverscaledTileID(7, 0, { 4, 2, 3 }));
EXPECT_FALSE(OverscaledTileID(4, 2, 3) < OverscaledTileID(4, 2, 3));
- EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(5, { 4, 2, 3 }));
- EXPECT_FALSE(OverscaledTileID(5, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3));
- EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(6, { 4, 2, 3 }));
- EXPECT_FALSE(OverscaledTileID(6, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3));
- EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(7, { 4, 2, 3 }));
- EXPECT_FALSE(OverscaledTileID(7, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3));
-
- EXPECT_EQ(8u, OverscaledTileID(7, { 4, 2, 3 }).overscaleFactor());
- EXPECT_EQ(4u, OverscaledTileID(6, { 4, 2, 3 }).overscaleFactor());
- EXPECT_EQ(2u, OverscaledTileID(5, { 4, 2, 3 }).overscaleFactor());
- EXPECT_EQ(1u, OverscaledTileID(4, { 4, 2, 3 }).overscaleFactor());
- EXPECT_EQ(2147483648u, OverscaledTileID(31, { 0, 0, 0 }).overscaleFactor());
-
- EXPECT_EQ(OverscaledTileID(0, { 0, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(0));
- EXPECT_EQ(OverscaledTileID(1, { 1, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(1));
- EXPECT_EQ(OverscaledTileID(2, { 2, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(2));
- EXPECT_EQ(OverscaledTileID(3, { 3, 1, 1 }), OverscaledTileID(4, 2, 3).scaledTo(3));
- EXPECT_EQ(OverscaledTileID(4, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(4));
- EXPECT_EQ(OverscaledTileID(5, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(5));
- EXPECT_EQ(OverscaledTileID(6, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(6));
- EXPECT_EQ(OverscaledTileID(7, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(7));
- EXPECT_EQ(OverscaledTileID(8, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(8));
- EXPECT_EQ(OverscaledTileID(32, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(32));
-
- EXPECT_EQ(UnwrappedTileID(0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).unwrapTo(0));
- EXPECT_EQ(UnwrappedTileID(-1, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).unwrapTo(-1));
- EXPECT_EQ(UnwrappedTileID(1, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).unwrapTo(1));
- EXPECT_EQ(UnwrappedTileID(0, { 4, 2, 3 }), OverscaledTileID(5, { 4, 2, 3 }).unwrapTo(0));
- EXPECT_EQ(UnwrappedTileID(-1, { 4, 2, 3 }), OverscaledTileID(5, { 4, 2, 3 }).unwrapTo(-1));
- EXPECT_EQ(UnwrappedTileID(1, { 4, 2, 3 }), OverscaledTileID(5, { 4, 2, 3 }).unwrapTo(1));
+ EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(5, 0, { 4, 2, 3 }));
+ EXPECT_FALSE(OverscaledTileID(5, 0, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3));
+ EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(6, 0, { 4, 2, 3 }));
+ EXPECT_FALSE(OverscaledTileID(6, 0, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3));
+ EXPECT_TRUE(OverscaledTileID(4, 2, 3) < OverscaledTileID(7,0, { 4, 2, 3 }));
+ EXPECT_FALSE(OverscaledTileID(7, 0, { 4, 2, 3 }) < OverscaledTileID(4, 2, 3));
+
+ EXPECT_EQ(8u, OverscaledTileID(7, 0, { 4, 2, 3 }).overscaleFactor());
+ EXPECT_EQ(4u, OverscaledTileID(6, 0, { 4, 2, 3 }).overscaleFactor());
+ EXPECT_EQ(2u, OverscaledTileID(5, 0, { 4, 2, 3 }).overscaleFactor());
+ EXPECT_EQ(1u, OverscaledTileID(4, 0, { 4, 2, 3 }).overscaleFactor());
+ EXPECT_EQ(2147483648u, OverscaledTileID(31, 0, { 0, 0, 0 }).overscaleFactor());
+
+ EXPECT_EQ(OverscaledTileID(0, 0, { 0, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(0));
+ EXPECT_EQ(OverscaledTileID(1, 0, { 1, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(1));
+ EXPECT_EQ(OverscaledTileID(2, 0, { 2, 0, 0 }), OverscaledTileID(4, 2, 3).scaledTo(2));
+ EXPECT_EQ(OverscaledTileID(3, 0, { 3, 1, 1 }), OverscaledTileID(4, 2, 3).scaledTo(3));
+ EXPECT_EQ(OverscaledTileID(4, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(4));
+ EXPECT_EQ(OverscaledTileID(5, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(5));
+ EXPECT_EQ(OverscaledTileID(6, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(6));
+ EXPECT_EQ(OverscaledTileID(7, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(7));
+ EXPECT_EQ(OverscaledTileID(8, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(8));
+ EXPECT_EQ(OverscaledTileID(32, 0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).scaledTo(32));
+
+ EXPECT_EQ(UnwrappedTileID(0, { 4, 2, 3 }), OverscaledTileID(4, 2, 3).toUnwrapped());
+ EXPECT_EQ(UnwrappedTileID(-1, { 4, 2, 3 }), OverscaledTileID(4, -1, { 4, 2, 3 }).toUnwrapped());
+ EXPECT_EQ(UnwrappedTileID(1, { 4, 2, 3 }), OverscaledTileID(4, 1, { 4, 2, 3 }).toUnwrapped());
+ EXPECT_EQ(UnwrappedTileID(0, { 4, 2, 3 }), OverscaledTileID(5, 0, { 4, 2, 3 }).toUnwrapped());
+ EXPECT_EQ(UnwrappedTileID(-1, { 4, 2, 3 }), OverscaledTileID(5, -1, { 4, 2, 3 }).toUnwrapped());
+ EXPECT_EQ(UnwrappedTileID(1, { 4, 2, 3 }), OverscaledTileID(5, 1, { 4, 2, 3 }).toUnwrapped());
EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(3, 1, 1)));
EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(3, 1, 1)));
EXPECT_TRUE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(3, 1, 1)));
- EXPECT_TRUE(OverscaledTileID(5, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1)));
- EXPECT_TRUE(OverscaledTileID(6, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1)));
- EXPECT_TRUE(OverscaledTileID(7, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1)));
-
- EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(5, { 4, 2, 3 })));
- EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(5, { 4, 2, 3 })));
- EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(5, { 4, 2, 3 })));
- EXPECT_FALSE(OverscaledTileID(5, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 2, 3 })));
- EXPECT_TRUE(OverscaledTileID(6, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 2, 3 })));
- EXPECT_TRUE(OverscaledTileID(7, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 2, 3 })));
-
- EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(6, { 4, 2, 3 })));
- EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(6, { 4, 2, 3 })));
- EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(6, { 4, 2, 3 })));
- EXPECT_FALSE(OverscaledTileID(5, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, { 4, 2, 3 })));
- EXPECT_FALSE(OverscaledTileID(6, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, { 4, 2, 3 })));
- EXPECT_TRUE(OverscaledTileID(7, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, { 4, 2, 3 })));
-
- EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(5, { 4, 0, 0 })));
- EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(5, { 4, 0, 0 })));
- EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(5, { 4, 0, 0 })));
- EXPECT_FALSE(OverscaledTileID(5, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 0, 0 })));
- EXPECT_FALSE(OverscaledTileID(6, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 0, 0 })));
- EXPECT_FALSE(OverscaledTileID(7, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 4, 0, 0 })));
-
- EXPECT_FALSE(OverscaledTileID(4, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, { 3, 1, 1 })));
+ EXPECT_TRUE(OverscaledTileID(5, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1)));
+ EXPECT_TRUE(OverscaledTileID(6, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1)));
+ EXPECT_TRUE(OverscaledTileID(7, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(3, 1, 1)));
+
+ EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 })));
+ EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 })));
+ EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 })));
+ EXPECT_FALSE(OverscaledTileID(5, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 })));
+ EXPECT_TRUE(OverscaledTileID(6, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 })));
+ EXPECT_TRUE(OverscaledTileID(7, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 2, 3 })));
+
+ EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 })));
+ EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 })));
+ EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 })));
+ EXPECT_FALSE(OverscaledTileID(5, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 })));
+ EXPECT_FALSE(OverscaledTileID(6, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 })));
+ EXPECT_TRUE(OverscaledTileID(7, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(6, 0, { 4, 2, 3 })));
+
+ EXPECT_FALSE(OverscaledTileID(2, 0, 0).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 })));
+ EXPECT_FALSE(OverscaledTileID(3, 1, 1).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 })));
+ EXPECT_FALSE(OverscaledTileID(4, 2, 3).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 })));
+ EXPECT_FALSE(OverscaledTileID(5, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 })));
+ EXPECT_FALSE(OverscaledTileID(6, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 })));
+ EXPECT_FALSE(OverscaledTileID(7, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 4, 0, 0 })));
+
+ EXPECT_FALSE(OverscaledTileID(4, 0, { 4, 2, 3 }).isChildOf(OverscaledTileID(5, 0, { 3, 1, 1 })));
}
TEST(TileID, Unwrapped) {
@@ -273,10 +273,10 @@ TEST(TileID, Unwrapped) {
EXPECT_FALSE(UnwrappedTileID(0, 1, 0) < UnwrappedTileID(1, 0, 0));
EXPECT_FALSE(UnwrappedTileID(5, 3, 6) < UnwrappedTileID(5, 3, 6));
- EXPECT_EQ(OverscaledTileID(4, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(4));
- EXPECT_EQ(OverscaledTileID(5, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(5));
- EXPECT_EQ(OverscaledTileID(6, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(6));
- EXPECT_EQ(OverscaledTileID(32, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(32));
+ EXPECT_EQ(OverscaledTileID(4, 0, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(4));
+ EXPECT_EQ(OverscaledTileID(5, 0, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(5));
+ EXPECT_EQ(OverscaledTileID(6, 0, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(6));
+ EXPECT_EQ(OverscaledTileID(32, 0, { 4, 2, 3 }), UnwrappedTileID(4, 2, 3).overscaleTo(32));
EXPECT_EQ(UnwrappedTileID(-1, { 1, 0, 0 }), UnwrappedTileID(1, -2, 0));
EXPECT_EQ(UnwrappedTileID(-1, { 1, 0, 1 }), UnwrappedTileID(1, -2, 1));
diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp
index f24733dc9b..9d42f7ae74 100644
--- a/test/tile/vector_tile.test.cpp
+++ b/test/tile/vector_tile.test.cpp
@@ -6,11 +6,11 @@
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/map/transform.hpp>
-#include <mbgl/map/query.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/buckets/symbol_bucket.hpp>
-#include <mbgl/text/collision_tile.hpp>
+#include <mbgl/renderer/query.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/renderer/image_manager.hpp>
@@ -26,7 +26,8 @@ public:
TransformState transformState;
util::RunLoop loop;
ThreadPool threadPool { 1 };
- AnnotationManager annotationManager;
+ style::Style style { loop, fileSource, 1 };
+ AnnotationManager annotationManager { style };
ImageManager imageManager;
GlyphManager glyphManager { fileSource };
Tileset tileset { { "https://example.com" }, { 0, 22 }, "none" };
@@ -40,7 +41,8 @@ public:
MapMode::Continuous,
annotationManager,
imageManager,
- glyphManager
+ glyphManager,
+ 0
};
};
@@ -56,7 +58,8 @@ TEST(VectorTile, setError) {
TEST(VectorTile, onError) {
VectorTileTest test;
VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, test.tileset);
- tile.onError(std::make_exception_ptr(std::runtime_error("test")));
+ tile.onError(std::make_exception_ptr(std::runtime_error("test")), 0);
+
EXPECT_FALSE(tile.isRenderable());
EXPECT_TRUE(tile.isLoaded());
EXPECT_TRUE(tile.isComplete());
@@ -67,12 +70,13 @@ TEST(VectorTile, Issue7615) {
VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, test.tileset);
style::SymbolLayer symbolLayer("symbol", "source");
+ std::vector<SymbolInstance> symbolInstances;
auto symbolBucket = std::make_shared<SymbolBucket>(
style::SymbolLayoutProperties::PossiblyEvaluated(),
std::map<
std::string,
std::pair<style::IconPaintProperties::PossiblyEvaluated, style::TextPaintProperties::PossiblyEvaluated>>(),
- 16.0f, 1.0f, 0.0f, false, false);
+ 16.0f, 1.0f, 0.0f, false, false, false, std::move(symbolInstances));
// Simulate placement of a symbol layer.
tile.onPlacement(GeometryTile::PlacementResult {
@@ -80,19 +84,16 @@ TEST(VectorTile, Issue7615) {
symbolLayer.getID(),
symbolBucket
}},
- nullptr,
{},
{},
- 0
- });
+ }, 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
- });
+ }, 0);
EXPECT_EQ(symbolBucket.get(), tile.getBucket(*symbolLayer.baseImpl));
}
diff --git a/test/util/dtoa.test.cpp b/test/util/dtoa.test.cpp
new file mode 100644
index 0000000000..8d2fba1877
--- /dev/null
+++ b/test/util/dtoa.test.cpp
@@ -0,0 +1,24 @@
+#include <mbgl/test/util.hpp>
+
+#include <mbgl/util/dtoa.hpp>
+
+#include <cfloat>
+#include <cmath>
+
+using namespace mbgl;
+
+TEST(Dtoa, Precision) {
+ EXPECT_EQ(M_E, std::stod(util::dtoa(M_E)));
+ EXPECT_EQ(M_LOG2E, std::stod(util::dtoa(M_LOG2E)));
+ EXPECT_EQ(M_LOG10E, std::stod(util::dtoa(M_LOG10E)));
+ EXPECT_EQ(M_LN2, std::stod(util::dtoa(M_LN2)));
+ EXPECT_EQ(M_LN10, std::stod(util::dtoa(M_LN10)));
+ EXPECT_EQ(M_PI, std::stod(util::dtoa(M_PI)));
+ EXPECT_EQ(M_PI_2, std::stod(util::dtoa(M_PI_2)));
+ EXPECT_EQ(M_PI_4, std::stod(util::dtoa(M_PI_4)));
+ EXPECT_EQ(M_1_PI, std::stod(util::dtoa(M_1_PI)));
+ EXPECT_EQ(M_2_PI, std::stod(util::dtoa(M_2_PI)));
+ EXPECT_EQ(M_2_SQRTPI, std::stod(util::dtoa(M_2_SQRTPI)));
+ EXPECT_EQ(M_SQRT2, std::stod(util::dtoa(M_SQRT2)));
+ EXPECT_EQ(M_SQRT1_2, std::stod(util::dtoa(M_SQRT1_2)));
+}
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 bca538c6c6..6befb521f0 100644
--- a/test/util/memory.test.cpp
+++ b/test/util/memory.test.cpp
@@ -3,9 +3,7 @@
#include <mbgl/test/util.hpp>
#include <mbgl/map/map.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
@@ -36,9 +34,6 @@ public:
}
util::RunLoop runLoop;
- HeadlessBackend backend { test::sharedDisplay() };
- BackendScope scope { backend };
- OffscreenView view{ backend.getContext(), { 512, 512 } };
StubFileSource fileSource;
ThreadPool threadPool { 4 };
@@ -73,21 +68,27 @@ private:
TEST(Memory, Vector) {
MemoryTest test;
+ float ratio { 2 };
- Map map(test.backend, { 256, 256 }, 2, test.fileSource, test.threadPool, MapMode::Still);
+ HeadlessFrontend frontend { { 256, 256 }, ratio, test.fileSource, test.threadPool };
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), ratio, test.fileSource,
+ test.threadPool, MapMode::Static);
map.setZoom(16); // more map features
map.getStyle().loadURL("mapbox://streets");
- test::render(map, test.view);
+ frontend.render(map);
}
TEST(Memory, Raster) {
MemoryTest test;
+ float ratio { 2 };
- Map map(test.backend, { 256, 256 }, 2, test.fileSource, test.threadPool, MapMode::Still);
+ HeadlessFrontend frontend { { 256, 256 }, ratio, test.fileSource, test.threadPool };
+ Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), ratio, test.fileSource,
+ test.threadPool, MapMode::Static);
map.getStyle().loadURL("mapbox://satellite");
- test::render(map, test.view);
+ frontend.render(map);
}
/**
@@ -114,46 +115,47 @@ TEST(Memory, Footprint) {
if (!shouldRunFootprint()) {
return;
}
-
+
MemoryTest test;
- auto renderMap = [&](Map& map, const char* style){
- map.setZoom(16);
- map.getStyle().loadURL(style);
- test::render(map, test.view);
+ class FrontendAndMap {
+ 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::Static) {
+ map.setZoom(16);
+ map.getStyle().loadURL(style);
+ frontend.render(map);
+ }
+
+ HeadlessFrontend frontend;
+ Map map;
};
// Warm up buffers and cache.
for (unsigned i = 0; i < 10; ++i) {
- Map map(test.backend, { 256, 256 }, 2, test.fileSource, test.threadPool, MapMode::Still);
- renderMap(map, "mapbox://streets");
- renderMap(map, "mapbox://satellite");
- };
+ FrontendAndMap(test, "mapbox://streets");
+ FrontendAndMap(test, "mapbox://satellite");
+ }
// Process close callbacks, mostly needed by
// libuv runloop.
test.runLoop.runOnce();
- std::vector<std::unique_ptr<Map>> maps;
+ std::vector<std::unique_ptr<FrontendAndMap>> maps;
unsigned runs = 15;
long vectorInitialRSS = mbgl::test::getCurrentRSS();
for (unsigned i = 0; i < runs; ++i) {
- auto vector = std::make_unique<Map>(test.backend, Size{ 256, 256 }, 2, test.fileSource,
- test.threadPool, MapMode::Still);
- renderMap(*vector, "mapbox://streets");
- maps.push_back(std::move(vector));
- };
+ maps.emplace_back(std::make_unique<FrontendAndMap>(test, "mapbox://streets"));
+ }
double vectorFootprint = (mbgl::test::getCurrentRSS() - vectorInitialRSS) / double(runs);
long rasterInitialRSS = mbgl::test::getCurrentRSS();
for (unsigned i = 0; i < runs; ++i) {
- auto raster = std::make_unique<Map>(test.backend, Size{ 256, 256 }, 2, test.fileSource,
- test.threadPool, MapMode::Still);
- renderMap(*raster, "mapbox://satellite");
- maps.push_back(std::move(raster));
- };
+ maps.emplace_back(std::make_unique<FrontendAndMap>(test, "mapbox://satellite"));
+ }
double rasterFootprint = (mbgl::test::getCurrentRSS() - rasterInitialRSS) / double(runs);
diff --git a/test/util/merge_lines.test.cpp b/test/util/merge_lines.test.cpp
index 6c8387c451..d3a2ebae03 100644
--- a/test/util/merge_lines.test.cpp
+++ b/test/util/merge_lines.test.cpp
@@ -1,4 +1,5 @@
#include <mbgl/test/util.hpp>
+#include <mbgl/test/stub_geometry_tile_feature.hpp>
#include <mbgl/layout/merge_lines.hpp>
#include <mbgl/layout/symbol_feature.hpp>
@@ -9,43 +10,22 @@ const std::u16string bbb = u"b";
using namespace mbgl;
-class GeometryTileFeatureStub : public GeometryTileFeature {
-public:
- GeometryTileFeatureStub(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_,
- std::unordered_map<std::string, Value> properties_) :
- id(std::move(id_)),
- type(type_),
- geometry(std::move(geometry_)),
- properties(std::move(properties_))
- {}
-
- FeatureType getType() const override { return type; }
- optional<Value> getValue(const std::string& key) const override {
- auto it = properties.find(key);
- if (it != properties.end()) {
- return it->second;
- }
- return {};
- };
- std::unordered_map<std::string,Value> getProperties() const override { return properties; };
- optional<FeatureIdentifier> getID() const override { return id; };
- GeometryCollection getGeometries() const override { return geometry; };
-
- optional<FeatureIdentifier> id;
- FeatureType type;
- GeometryCollection geometry;
- std::unordered_map<std::string,Value> properties;
-};
+namespace {
+
+PropertyMap properties;
+LineString<int16_t> emptyLine;
+
+}
class SymbolFeatureStub : public SymbolFeature {
public:
SymbolFeatureStub(optional<FeatureIdentifier> id_, FeatureType type_, GeometryCollection geometry_,
- std::unordered_map<std::string, Value> properties_, optional<std::u16string> text_,
- optional<std::string> icon_, std::size_t index_) :
- SymbolFeature(std::make_unique<GeometryTileFeatureStub>(id_, type_, geometry_, properties_))
+ PropertyMap properties_, optional<std::u16string> text_,
+ optional<std::string> icon_, std::size_t index_) :
+ SymbolFeature(std::make_unique<StubGeometryTileFeature>(std::move(id_), type_, std::move(geometry_), std::move(properties_)))
{
- text = text_;
- icon = icon_;
+ text = std::move(text_);
+ icon = std::move(icon_);
index = index_;
}
};
@@ -53,20 +33,20 @@ public:
TEST(MergeLines, SameText) {
// merges lines with the same text
std::vector<mbgl::SymbolFeature> input1;
- input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0));
- input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {}, bbb, {}, 0));
- input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{8, 0}, {9, 0}}}, {}, aaa, {}, 0));
- input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0));
- input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, {}, aaa, {}, 0));
- input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{5, 0}, {6, 0}}}, {}, aaa, {}, 0));
-
- const std::vector<GeometryTileFeatureStub> expected1 = {
- { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, {} },
- { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {} },
- { {}, FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, {} },
- { {}, FeatureType::LineString, {{}}, {} },
- { {}, FeatureType::LineString, {{}}, {} },
- { {}, FeatureType::LineString, {{}}, {} }
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, properties, aaa, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, properties, bbb, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{8, 0}, {9, 0}}}, properties, aaa, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, properties, aaa, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, properties, aaa, {}, 0));
+ input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{5, 0}, {6, 0}}}, properties, aaa, {}, 0));
+
+ const std::vector<StubGeometryTileFeature> expected1 = {
+ { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, properties },
+ { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, properties },
+ { {}, FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, properties },
+ { {}, FeatureType::LineString, { emptyLine }, properties },
+ { {}, FeatureType::LineString, { emptyLine }, properties },
+ { {}, FeatureType::LineString, { emptyLine }, properties }
};
mbgl::util::mergeLines(input1);
@@ -79,14 +59,14 @@ TEST(MergeLines, SameText) {
TEST(MergeLines, BothEnds) {
// mergeLines handles merge from both ends
std::vector<mbgl::SymbolFeature> input2;
- input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0 });
- input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {}, aaa, {}, 0 });
- input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0 });
-
- const std::vector<GeometryTileFeatureStub> expected2 = {
- { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, {} },
- { {}, FeatureType::LineString, {{}}, {} },
- { {}, FeatureType::LineString, {{}}, {} }
+ input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, properties, aaa, {}, 0 });
+ input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, properties, aaa, {}, 0 });
+ input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, properties, aaa, {}, 0 });
+
+ const std::vector<StubGeometryTileFeature> expected2 = {
+ { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, properties },
+ { {}, FeatureType::LineString, { emptyLine }, properties },
+ { {}, FeatureType::LineString, { emptyLine }, properties }
};
mbgl::util::mergeLines(input2);
@@ -99,14 +79,14 @@ TEST(MergeLines, BothEnds) {
TEST(MergeLines, CircularLines) {
// mergeLines handles circular lines
std::vector<mbgl::SymbolFeature> input3;
- input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0 });
- input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0 });
- input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {0, 0}}}, {}, aaa, {}, 0 });
-
- const std::vector<GeometryTileFeatureStub> expected3 = {
- { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, {} },
- { {}, FeatureType::LineString, {{}}, {} },
- { {}, FeatureType::LineString, {{}}, {} }
+ input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, properties, aaa, {}, 0 });
+ input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, properties, aaa, {}, 0 });
+ input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {0, 0}}}, properties, aaa, {}, 0 });
+
+ const std::vector<StubGeometryTileFeature> expected3 = {
+ { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, properties },
+ { {}, FeatureType::LineString, { emptyLine }, properties },
+ { {}, FeatureType::LineString, { emptyLine }, properties }
};
mbgl::util::mergeLines(input3);
@@ -118,9 +98,9 @@ TEST(MergeLines, CircularLines) {
TEST(MergeLines, EmptyOuterGeometry) {
std::vector<mbgl::SymbolFeature> input;
- input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {}, {}, aaa, {}, 0 });
+ input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {}, properties, aaa, {}, 0 });
- const std::vector<GeometryTileFeatureStub> expected = { { {}, FeatureType::LineString, {}, {} } };
+ const std::vector<StubGeometryTileFeature> expected = { { {}, FeatureType::LineString, {}, properties } };
mbgl::util::mergeLines(input);
@@ -129,9 +109,9 @@ TEST(MergeLines, EmptyOuterGeometry) {
TEST(MergeLines, EmptyInnerGeometry) {
std::vector<mbgl::SymbolFeature> input;
- input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{}}, {}, aaa, {}, 0 });
+ input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {}, properties, aaa, {}, 0 });
- const std::vector<GeometryTileFeatureStub> expected = { { {}, FeatureType::LineString, {{}}, {} } };
+ const std::vector<StubGeometryTileFeature> expected = { { {}, FeatureType::LineString, {}, properties } };
mbgl::util::mergeLines(input);
diff --git a/test/util/offscreen_texture.test.cpp b/test/util/offscreen_texture.test.cpp
index 8c0d4f7011..09c940c4c3 100644
--- a/test/util/offscreen_texture.test.cpp
+++ b/test/util/offscreen_texture.test.cpp
@@ -3,28 +3,26 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
-#include <mbgl/map/backend_scope.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
#include <mbgl/util/offscreen_texture.hpp>
using namespace mbgl;
TEST(OffscreenTexture, EmptyRed) {
- HeadlessBackend backend { test::sharedDisplay() };
+ HeadlessBackend backend({ 512, 256 });
BackendScope scope { backend };
- OffscreenView view(backend.getContext(), { 512, 256 });
- // Scissor test shouldn't leak after OffscreenView::bind().
+ // Scissor test shouldn't leak after HeadlessBackend::bind().
MBGL_CHECK_ERROR(glScissor(64, 64, 128, 128));
backend.getContext().scissorTest.setCurrentValue(true);
- view.bind();
+ backend.bind();
MBGL_CHECK_ERROR(glClearColor(1.0f, 0.0f, 0.0f, 1.0f));
MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT));
- auto image = view.readStillImage();
+ auto image = backend.readStillImage();
test::checkImage("test/fixtures/offscreen_texture/empty-red", image, 0, 0);
}
@@ -74,7 +72,7 @@ struct Buffer {
TEST(OffscreenTexture, RenderToTexture) {
- HeadlessBackend backend { test::sharedDisplay() };
+ HeadlessBackend backend({ 512, 256 });
BackendScope scope { backend };
auto& context = backend.getContext();
@@ -124,8 +122,7 @@ void main() {
Buffer triangleBuffer({ 0, 0.5, 0.5, -0.5, -0.5, -0.5 });
Buffer viewportBuffer({ -1, -1, 1, -1, -1, 1, 1, 1 });
- OffscreenView view(context, { 512, 256 });
- view.bind();
+ backend.bind();
// First, draw red to the bound FBO.
context.clear(Color::red(), {}, {});
@@ -153,9 +150,9 @@ void main() {
test::checkImage("test/fixtures/offscreen_texture/render-to-texture", image, 0, 0);
// Now reset the FBO back to normal and retrieve the original (restored) framebuffer.
- view.bind();
+ backend.bind();
- image = view.readStillImage();
+ image = backend.readStillImage();
test::checkImage("test/fixtures/offscreen_texture/render-to-fbo", image, 0, 0);
// Now, composite the Framebuffer texture we've rendered to onto the main FBO.
@@ -168,6 +165,6 @@ void main() {
glVertexAttribPointer(compositeShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
- image = view.readStillImage();
+ image = backend.readStillImage();
test::checkImage("test/fixtures/offscreen_texture/render-to-fbo-composited", image, 0, 0.1);
}
diff --git a/test/util/thread_local.test.cpp b/test/util/thread_local.test.cpp
index 0590e8b4dc..7142697f48 100644
--- a/test/util/thread_local.test.cpp
+++ b/test/util/thread_local.test.cpp
@@ -71,38 +71,38 @@ TEST(ThreadLocalStorage, NotSetReturnsNull) {
namespace {
-struct DtorCounter {
- ~DtorCounter() { ++(*value); }
- unsigned *value;
-};
-
-class TestThreadReclaim {
+class TestThreadDataOwnership {
public:
- TestThreadReclaim(mbgl::ActorRef<TestThreadReclaim>, DtorCounter* counter_) {
- counter.set(counter_);
+ TestThreadDataOwnership(mbgl::ActorRef<TestThreadDataOwnership>, int* data_) {
+ data.set(data_);
+ }
+
+ ~TestThreadDataOwnership() {
+ data.set(nullptr);
}
private:
- static ThreadLocal<DtorCounter> counter;
+ static ThreadLocal<int> data;
};
-ThreadLocal<DtorCounter> TestThreadReclaim::counter;
+ThreadLocal<int> TestThreadDataOwnership::data;
} // namespace
-TEST(ThreadLocalStorage, AutoReclaim) {
+TEST(ThreadLocalStorage, ShouldNotTakeOwnership) {
RunLoop loop;
- unsigned counter = 0;
-
- auto dtorCounter1 = new DtorCounter{ &counter };
- auto dtorCounter2 = new DtorCounter{ &counter };
+ auto data1 = std::make_unique<int>(10);
+ auto data2 = std::make_unique<int>(20);
- auto thread1 = std::make_unique<Thread<TestThreadReclaim>>("Test", dtorCounter1);
- auto thread2 = std::make_unique<Thread<TestThreadReclaim>>("Test", dtorCounter2);
+ auto thread1 = std::make_unique<Thread<TestThreadDataOwnership>>("Test", data1.get());
+ auto thread2 = std::make_unique<Thread<TestThreadDataOwnership>>("Test", data2.get());
thread1.reset();
thread2.reset();
- EXPECT_EQ(counter, 2u);
+ // Will crash if ThreadLocal destroys
+ // the pointer it is managing.
+ ASSERT_EQ(*data1, 10);
+ ASSERT_EQ(*data2, 20);
}
diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp
index c746e6dab5..933c18b5ea 100644
--- a/test/util/tile_cover.test.cpp
+++ b/test/util/tile_cover.test.cpp
@@ -84,3 +84,12 @@ TEST(TileCover, SanFranciscoZ0Wrapped) {
EXPECT_EQ((std::vector<UnwrappedTileID>{ { 0, 1, 0 } }),
util::tileCover(sanFranciscoWrapped, 0));
}
+
+TEST(TileCount, SanFranciscoZ10) {
+ EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10, util::tileSize));
+}
+
+TEST(TileCount, SanFranciscoZ22) {
+ EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22, util::tileSize));
+}
+
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);
+}